From: Sejun Park Date: Fri, 24 Nov 2017 08:12:58 +0000 (+0900) Subject: Initial version of libomxil-vc4 for RPI3 X-Git-Tag: submit/tizen_4.0/20171213.043831^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c37e0552046d8f3dd7dad004003411ef6c9f7c76;p=platform%2Fadaptation%2Fbroadcom%2Flibomxil-vc4.git Initial version of libomxil-vc4 for RPI3 build: changed build specs. Changed packaging names as below: libomxil-vc4-0.0.1-0.1.armv7l.rpm libomxil-vc4-debuginfo-0.0.1-0.1.armv7l.rpm libomxil-vc4-debugsource-0.0.1-0.1.armv7l.rpm libomxil-vc4-devel-0.0.1-0.1.armv7l.rpm libomxil-vc4-utils-0.0.1-0.1.armv7l.rpm libomxil-vc4-utils-debuginfo-0.0.1-0.1.armv7l.rpm Change-Id: I53d1515c6af3ba427740fc57479608c4e5abd98e Signed-off-by: Hackseung Lee --- diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..63570f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Build directory +build/ + +# Compiled Object files +*.slo +*.lo +*.o + +# Compiled Dynamic libraries +*.so + +# Compiled Static libraries +*.lai +*.la +*.a + +*.raw +*.rgb +*.bgr +*.h264 +*.264 +*.yuyv +*.uyvy +*.yvyu +*.vyuy +*.i420 +*.yuv +*.jpg +*.avi +*.pts +*.ppm +*.mkv diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..cfc8ae5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,132 @@ +cmake_minimum_required(VERSION 2.8) + +project(vmcs_host_apps) + +SET(PROJECT_VER_MAJOR 1) +SET(PROJECT_VER_MINOR 0) +SET(PROJECT_VER_PATCH 0) +SET(PROJECT_VER "${PROJECT_VER_MAJOR}.${PROJECT_VER_MINOR}.${PROJECT_VER_PATCH}") +SET(PROJECT_APIVER "${PROJECT_VER}") + +if(ARM64) + set(BUILD_MMAL FALSE) + set(BUILD_MMAL_APPS FALSE) +else() + set(BUILD_MMAL TRUE) + set(BUILD_MMAL_APPS TRUE) +endif() +set(vmcs_root ${PROJECT_SOURCE_DIR}) +get_filename_component(VIDEOCORE_ROOT . ABSOLUTE) + +set(VCOS_PTHREADS_BUILD_SHARED TRUE) + +include(makefiles/cmake/global_settings.cmake) +include(makefiles/cmake/arm-linux.cmake) +include(makefiles/cmake/vmcs.cmake) + +enable_language(ASM) + +# Global include paths +include_directories(host_applications/framework) +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(interface/vcos/pthreads) +include_directories(interface/vmcs_host/linux) +include_directories(interface/vmcs_host) +include_directories(interface/vmcs_host/khronos) +include_directories(interface/khronos/include) +include_directories(${PROJECT_BINARY_DIR}) +include_directories(interface/vchiq_arm) +#include_directories(tools/inet_transport) +include_directories(host_support/include) + +# Global compiler flags +if(CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-multichar -Wall -Wno-unused-but-set-variable -fPIC") +endif() + +add_definitions(-D_REENTRANT) +add_definitions(-DUSE_VCHIQ_ARM -DVCHI_BULK_ALIGN=1 -DVCHI_BULK_GRANULARITY=1) +add_definitions(-DOMX_SKIP64BIT) +add_definitions(-DEGL_SERVER_DISPMANX) +add_definitions(-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) +add_definitions(-D_GNU_SOURCE) + +# do we actually need this? +add_definitions(-D__VIDEOCORE4__) +add_definitions(-DTV_SUPPORTED_MODE_NO_DEPRECATED) + +# add_definitions(-DKHRONOS_CLIENT_LOGGING) + +# Check for OpenWF-C value set via command line +if(KHRONOS_EGL_PLATFORM MATCHES "openwfc") + add_definitions(-DKHRONOS_EGL_PLATFORM_OPENWFC) +endif() + +# List of subsidiary CMakeLists +add_subdirectory(interface/vcos) +add_subdirectory(interface/vmcs_host) +add_subdirectory(interface/vchiq_arm) +if(NOT ARM64) + add_subdirectory(interface/khronos) +endif() + +#add_subdirectory(opensrc/tools/lua) +if(BUILD_MMAL) + include_directories(interface/mmal) + add_subdirectory(interface/mmal) + add_subdirectory(containers) +endif() + +# VidTex supports Android and Linux +if(BUILD_MMAL_APPS) +add_subdirectory(host_applications/android/apps/vidtex) +endif(BUILD_MMAL_APPS) + +if(NOT ARM64) + add_subdirectory(middleware/openmaxil) +endif() + +# 3d demo code +#if(NOT ANDROID) +# add_subdirectory(thirdparty/applications/demos) +# add_subdirectory(opensrc/applications/demos) +#endif() + +#if(ENABLE_3D_TESTS) +# add_subdirectory(thirdparty/applications/test) +#endif() + +# FIXME: we should use a pre-packaged version of freetype +# rather than the one included in the repo. +#add_subdirectory(opensrc/helpers/freetype) +#add_subdirectory(${PROJECT_SOURCE_DIR}/opensrc/helpers/fonts/ttf-bitstream-vera) + +# VMCS Host Applications +#add_subdirectory(host_applications/framework) + +# add_subdirectory(interface/vchiq/test/win32) + +# Apps and libraries supporting Camera Tuning Tool +#add_subdirectory(tools/inet_transport/linux) +#add_subdirectory(host_support/vcstandalone) + +# add linux apps +add_subdirectory(host_applications/linux) +add_subdirectory(opensrc/helpers/libfdt) +add_subdirectory(helpers/dtoverlay) + +set(vmcs_host_apps_VERSION_MAJOR 1) +set(vmcs_host_apps_VERSION_MINOR 0) + +include_directories("${PROJECT_BINARY_DIR}") +include(FindPkgConfig QUIET) +if(PKG_CONFIG_FOUND) + # Produce a pkg-config file + foreach(PCFILE bcm_host.pc egl.pc glesv2.pc vg.pc brcmegl.pc brcmglesv2.pc brcmvg.pc vcsm.pc mmal.pc ) + configure_file("pkgconfig/${PCFILE}.in" "${PCFILE}" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PCFILE}" + DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig") + endforeach() +endif() +# Remove cache entry, if one added by command line +unset(KHRONOS_EGL_PLATFORM CACHE) diff --git a/COPYING b/COPYING new file mode 100755 index 0000000..a60e55d --- /dev/null +++ b/COPYING @@ -0,0 +1,21 @@ +Copyright (C) 2017 Samsung Electronics co., Ltd. All Rights Reserved. + +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, sub license, 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 (including the +next paragraph) 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 NON-INFRINGEMENT. +IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS 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. diff --git a/LICENCE b/LICENCE new file mode 100755 index 0000000..dea4c26 --- /dev/null +++ b/LICENCE @@ -0,0 +1,26 @@ +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2015, Raspberry Pi (Trading) Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README.md b/README.md new file mode 100755 index 0000000..358d2b4 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +This repository contains the source code for the ARM side libraries used on Raspberry Pi. +These typically are installed in /opt/vc/lib and includes source for the ARM side code to interface to: +EGL, mmal, GLESv2, vcos, openmaxil, vchiq_arm, bcm_host, WFC, OpenVG. + +Use buildme to build. It requires cmake to be installed and an arm cross compiler. It is set up to use this one: +https://github.com/raspberrypi/tools/tree/master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian diff --git a/buildme b/buildme new file mode 100755 index 0000000..b8fd440 --- /dev/null +++ b/buildme @@ -0,0 +1,44 @@ +#!/bin/bash +BUILDTYPE=Release + +if [ "$1" = "--debug" ]; then + BUILDTYPE=Debug + shift +fi + +BUILDSUBDIR=`echo $BUILDTYPE | tr '[A-Z]' '[a-z]'`; + +if [ "armv6l" = `arch` ] || [ "armv7l" = `arch` ]; then + # Native compile on the Raspberry Pi + mkdir -p build/raspberry/$BUILDSUBDIR + pushd build/raspberry/$BUILDSUBDIR + cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE ../../.. + if [ "armv6l" = `arch` ]; then + make + else + make -j4 + fi + if [ "$1" != "" ]; then + sudo make install DESTDIR=$1 + else + sudo make install + fi +elif [ "$1" = "--native" ]; then + # Build natively on the host + mkdir -p build/native/$BUILDSUBDIR + pushd build/native/$BUILDSUBDIR + cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE ../../.. + shift + make -j `nproc` $* +else + # Cross compile on a more capable machine + mkdir -p build/arm-linux/$BUILDSUBDIR + pushd build/arm-linux/$BUILDSUBDIR + cmake -DCMAKE_TOOLCHAIN_FILE=../../../makefiles/cmake/toolchains/arm-linux-gnueabihf.cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE ../../.. + make -j `nproc` + + if [ "$1" != "" ]; then + sudo make install DESTDIR=$1 + fi +fi +popd diff --git a/containers/CMakeLists.txt b/containers/CMakeLists.txt new file mode 100755 index 0000000..5570038 --- /dev/null +++ b/containers/CMakeLists.txt @@ -0,0 +1,124 @@ +SET( SOURCE_DIR . ) + +# We support building both static and shared libraries +if (NOT DEFINED LIBRARY_TYPE) +set(LIBRARY_TYPE SHARED) +endif (NOT DEFINED LIBRARY_TYPE) + +# Make sure the compiler can find the necessary include files +include_directories (${SOURCE_DIR}/.. ${SOURCE_DIR}/../interface/vcos) + +# Needed for the container loader +add_definitions(-DDL_PATH_PREFIX="${VMCS_PLUGIN_DIR}/") + +SET( GCC_COMPILER_FLAGS -Wall -g -O2 -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wcast-qual -Wwrite-strings -Wundef ) +SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wextra )#-Wno-missing-field-initializers ) +SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -std=c99 -D_POSIX_C_SOURCE=200112L ) +SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wno-missing-field-initializers ) +SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wno-unused-value ) + +add_definitions( ${GCC_COMPILER_FLAGS} ) + +# Containers core library +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_io.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_io_helpers.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_codecs.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_utils.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_writer_utils.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_loader.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_filters.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_logging.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_uri.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_bits.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_list.c) +set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_index.c) + +# Containers io library +set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_file.c) +set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_null.c) +set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_net.c) +set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_pktfile.c) +set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_http.c) +add_definitions( -DENABLE_CONTAINER_IO_HTTP ) + +# Containers net library +if (DEFINED MSVC) +set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_common.c) +set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_win32.c) +elseif (DEFINED LINUX OR DEFINED UNIX) +set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_common.c) +set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_bsd.c) +else (DEFINED MSVC) +set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_null.c) +endif (DEFINED MSVC) +set(extra_net_SRCS net_sockets_win32.c net_sockets_win32.h net_sockets_null.c) +add_custom_target(containers_net_extra ALL + COMMAND touch ${extra_net_SRCS} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/containers/net) + +# Packetizers library +set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/core/packetizers.c) +set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/mpga/mpga_packetizer.c) +set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/mpgv/mpgv_packetizer.c) +set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/pcm/pcm_packetizer.c) +set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/h264/avc1_packetizer.c) + +add_library(containers ${LIBRARY_TYPE} ${core_SRCS} ${io_SRCS} ${net_SRCS} ${packetizers_SRCS}) +target_link_libraries(containers vcos) +install(TARGETS containers DESTINATION lib) + +set(container_readers) +set(container_writers) + +# Container modules +add_subdirectory(mp4) +set(container_readers ${container_readers} reader_mp4) +set(container_writers ${container_writers} writer_mp4) +add_subdirectory(mpeg) +set(container_readers ${container_readers} reader_ps) +add_subdirectory(mpga) +set(container_readers ${container_readers} reader_mpga) +add_subdirectory(binary) +set(container_readers ${container_readers} reader_binary) +set(container_writers ${container_writers} writer_binary) +add_subdirectory(mkv) +set(container_readers ${container_readers} reader_mkv) +add_subdirectory(wav) +set(container_readers ${container_readers} reader_wav) +add_subdirectory(asf) +set(container_readers ${container_readers} reader_asf) +set(container_writers ${container_writers} writer_asf) +add_subdirectory(flash) +set(container_readers ${container_readers} reader_flv) +add_subdirectory(avi) +set(container_readers ${container_readers} reader_avi) +set(container_writers ${container_writers} writer_avi) +add_subdirectory(rtp) +set(container_readers ${container_readers} reader_rtp) +add_subdirectory(rtsp) +set(container_readers ${container_readers} reader_rtsp) +add_subdirectory(rcv) +set(container_readers ${container_readers} reader_rcv) +add_subdirectory(rv9) +set(container_readers ${container_readers} reader_rv9) +add_subdirectory(qsynth) +set(container_readers ${container_readers} reader_qsynth) +add_subdirectory(simple) +set(container_readers ${container_readers} reader_simple) +set(container_writers ${container_writers} writer_simple) +add_subdirectory(raw) +set(container_readers ${container_readers} reader_raw_video) +set(container_writers ${container_writers} writer_raw_video) +add_subdirectory(dummy) +set(container_writers ${container_writers} writer_dummy) + +add_subdirectory(metadata/id3) +set(container_readers ${container_readers} reader_metadata_id3) + +if (${LIBRARY_TYPE} STREQUAL STATIC) +target_link_libraries(containers ${container_readers} ${container_writers}) +endif (${LIBRARY_TYPE} STREQUAL STATIC) + +# Test apps +add_subdirectory(test) diff --git a/containers/asf/CMakeLists.txt b/containers/asf/CMakeLists.txt new file mode 100755 index 0000000..2cd7ac2 --- /dev/null +++ b/containers/asf/CMakeLists.txt @@ -0,0 +1,19 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_asf ${LIBRARY_TYPE} asf_reader.c) + +target_link_libraries(reader_asf containers) + +install(TARGETS reader_asf DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_asf ${LIBRARY_TYPE} asf_writer.c) + +target_link_libraries(writer_asf containers) + +install(TARGETS writer_asf DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/asf/asf_reader.c b/containers/asf/asf_reader.c new file mode 100755 index 0000000..c36ed5e --- /dev/null +++ b/containers/asf/asf_reader.c @@ -0,0 +1,2247 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +/* prevent double-defines when it is defined on the command line - as in the test app */ +#ifndef ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT +#endif +#ifndef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#endif + +#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->object_level + +/* For the sanity of the Visual Studio debugger make local names for structures */ +#define VC_CONTAINER_TRACK_MODULE_T ASF_VC_CONTAINER_TRACK_MODULE_T +#define VC_CONTAINER_MODULE_T ASF_VC_CONTAINER_MODULE_T +#define VC_CONTAINER_T ASF_VC_CONTAINER_T + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define ASF_TRACKS_MAX 2 +#define ASF_EXTRADATA_MAX 256 + +#define ASF_MAX_OBJECT_LEVEL 4 +#define ASF_MAX_CONSECUTIVE_UNKNOWN_OBJECTS 5 +#define ASF_MAX_OBJECT_SIZE (1<<29) /* Does not apply to the data object */ +#define ASF_OBJECT_HEADER_SIZE (16+8) +#define ASF_UNKNOWN_PTS ((uint32_t)(-1)) +#define ASF_MAX_CONSECUTIVE_CORRUPTED_PACKETS 100 +#define ASF_MAX_SEARCH_PACKETS 1000 + +#define ASF_SKIP_GUID(ctx, size, n) (size -= 16, SKIP_GUID(ctx,n)) +#define ASF_SKIP_U8(ctx, size, n) (size -= 1, SKIP_U8(ctx,n)) +#define ASF_SKIP_U16(ctx, size, n) (size -= 2, SKIP_U16(ctx,n)) +#define ASF_SKIP_U24(ctx, size, n) (size -= 3, SKIP_U24(ctx,n)) +#define ASF_SKIP_U32(ctx, size, n) (size -= 4, SKIP_U32(ctx,n)) +#define ASF_SKIP_U64(ctx, size, n) (size -= 8, SKIP_U64(ctx,n)) +#define ASF_READ_GUID(ctx, size, buffer, n) (size -= 16, READ_GUID(ctx,(uint8_t *)buffer,n)) +#define ASF_READ_U8(ctx, size, n) (size -= 1, READ_U8(ctx,n)) +#define ASF_READ_U16(ctx, size, n) (size -= 2, READ_U16(ctx,n)) +#define ASF_READ_U24(ctx, size, n) (size -= 3, READ_U24(ctx,n)) +#define ASF_READ_U32(ctx, size, n) (size -= 4, READ_U32(ctx,n)) +#define ASF_READ_U64(ctx, size, n) (size -= 8, READ_U64(ctx,n)) +#define ASF_READ_STRING(ctx, size, buffer, to_read, n) (size -= to_read, READ_STRING_UTF16(ctx,buffer,to_read,n)) +#define ASF_SKIP_STRING(ctx, size, to_read, n) (size -= to_read, SKIP_STRING_UTF16(ctx,to_read,n)) +#define ASF_READ_BYTES(ctx, size, buffer, to_read) (size -= to_read, READ_BYTES(ctx,buffer,to_read)) +#define ASF_SKIP_BYTES(ctx, size, to_read) (size -= to_read, SKIP_BYTES(ctx,to_read)) + +/* Read variable length field from p_context. */ +#define READ_VLC(p_context, length, value_if_missing, txt) \ + (length) == 1 ? READ_U8(p_context, txt) : \ + (length) == 2 ? READ_U16(p_context, txt) : \ + (length) == 3 ? READ_U32(p_context, txt) : value_if_missing + +#define CHECK_POINT(p_context, amount_to_read) do { \ + if(amount_to_read < 0) return VC_CONTAINER_ERROR_CORRUPTED; \ + if(STREAM_STATUS(p_context)) return STREAM_STATUS(p_context); } while(0) + +/****************************************************************************** +Type definitions. +******************************************************************************/ + +/** Context for our reader + */ +typedef struct +{ + uint64_t start; /* The byte offset start of the current packet in the file */ + uint32_t size; + uint32_t padding_size; + uint64_t send_time; /* read in mS, stored in uS */ + bool eos; + bool corrupted; + uint16_t bad_packets; + + /* All the different Length Types for the VLC codes */ + unsigned int replicated_data_lt; + unsigned int offset_into_media_object_lt; + unsigned int media_object_number_lt; + unsigned int payload_lt; + + unsigned int multiple_payloads; + unsigned int compressed_payloads; + + uint8_t num_payloads; + uint8_t current_payload; + uint32_t current_offset; /* The offset in the current packet for the next byte to be read */ + + /* Info already read */ + uint32_t stream_num; /* Stream number and key-frame flag */ + uint32_t media_object_num; + uint32_t media_object_off; + uint32_t payload_size; + uint32_t subpayload_size; + + /* Info read from the replicated data */ + uint32_t media_object_size; + uint64_t media_object_pts; /**< Presentation timestamp in microseconds */ + uint64_t media_object_pts_delta; /**< Presentation timestamp delta in microseconds */ + +} ASF_PACKET_STATE; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + /* The ID of the stream (the index in the containing array need not be the ID) */ + unsigned int stream_id; + bool b_valid; + + uint8_t extradata[ASF_EXTRADATA_MAX]; + + ASF_PACKET_STATE *p_packet_state; + ASF_PACKET_STATE local_packet_state; + + /* Simple index structure. Corresponds to the simple index in 6.1 of the spec + * This index has locations in packets, not in bytes, and relies on the + * file having fixed-length packets - as is required */ + struct + { + uint64_t offset; /**< Offset to the start of the simple index data */ + uint32_t num_entries; + int64_t time_interval; /* in uS */ + bool incomplete; /* The index does not go to the end of the file */ + } simple_index; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + int object_level; + + uint32_t packet_size; /**< Size of a data packet */ + uint64_t packets_num; /**< Number of packets contained in the data object */ + + bool broadcast; /**< Specifies if we are dealing with a broadcast stream */ + int64_t duration; /**< Duration of the stream in microseconds */ + int64_t preroll; /**< Duration of the preroll in microseconds. */ + /* This is the PTS of the first packet; all are offset by this amount. */ + uint64_t time_offset; /**< Offset added to timestamps in microseconds */ + + uint64_t data_offset; /**< Offset to the start of the data packets */ + int64_t data_size; /**< Size of the data contained in the data object */ + + /* The track objects. There's a count of these in VC_CONTAINER_T::tracks_num */ + VC_CONTAINER_TRACK_T *tracks[ASF_TRACKS_MAX]; + + /* Translation table from stream_number to index in the tracks array */ + unsigned char stream_number_to_index[128]; + + /* Data for a top-level index structure as defined in 6.2 of the spec */ + struct + { + uint64_t entry_time_interval; /* The time interval between specifiers, scaled to uS */ + uint32_t specifiers_count; /* The number of specifiers in the file, 0 if no index */ + uint64_t active_specifiers[ASF_TRACKS_MAX]; /* the specifier in use for each track, + * or >=specifiers_count if none */ + uint64_t specifiers_offset; /* The file address of the first specifier. */ + uint32_t block_count; /* The number of index blocks */ + uint64_t blocks_offset; /* The file address of the first block */ + } top_level_index; + + /* A pointer to the track (in the tracks array) which is to be used with a simple index. + * null if there is no such track */ + ASF_VC_CONTAINER_TRACK_MODULE_T *simple_index_track; + + /* Shared packet state. This is used when the tracks are in sync, + and for the track at the earliest position in the file when they are not in sync */ + ASF_PACKET_STATE packet_state; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Prototypes for local functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T asf_read_object( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_header( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_header_ext( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_file_properties( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_ext_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_simple_index( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_index( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_index_parameters( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_data( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_codec_list( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_content_description( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_ext_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_read_object_adv_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T asf_skip_unprocessed_object( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T seek_to_positions(VC_CONTAINER_T *p_ctx, + uint64_t track_positions[ASF_TRACKS_MAX], int64_t *p_time, + VC_CONTAINER_SEEK_FLAGS_T flags, unsigned int start_track, + bool seek_on_start_track); + +/****************************************************************************** +GUID list for the different ASF objects +******************************************************************************/ +static const GUID_T asf_guid_header = {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_file_props = {0x8CABDCA1, 0xA947, 0x11CF, {0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_stream_props = {0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_ext_stream_props = {0x14E6A5CB, 0xC672, 0x4332, {0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}}; +static const GUID_T asf_guid_data = {0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_simple_index = {0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}}; +static const GUID_T asf_guid_index = {0xD6E229D3, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; +static const GUID_T asf_guid_index_parameters = {0xD6E229DF, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; +static const GUID_T asf_guid_header_ext = {0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_codec_list = {0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}; +static const GUID_T asf_guid_content_description = {0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_ext_content_description = {0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}}; +static const GUID_T asf_guid_stream_bitrate_props = {0x7BF875CE, 0x468D, 0x11D1, {0x8D, 0x82, 0x00, 0x60, 0x97, 0xC9, 0xA2, 0xB2}}; +static const GUID_T asf_guid_language_list = {0x7C4346A9, 0xEFE0, 0x4BFC, {0xB2, 0x29, 0x39, 0x3E, 0xDE, 0x41, 0x5C, 0x85}}; +static const GUID_T asf_guid_metadata = {0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}}; +static const GUID_T asf_guid_padding = {0x1806D474, 0xCADF, 0x4509, {0xA4, 0xBA, 0x9A, 0xAB, 0xCB, 0x96, 0xAA, 0xE8}}; +static const GUID_T asf_guid_content_encryption = {0x2211B3FB, 0xBD23, 0x11D2, {0xB4, 0xB7, 0x00, 0xA0, 0xC9, 0x55, 0xFC, 0x6E}}; +static const GUID_T asf_guid_ext_content_encryption = {0x298AE614, 0x2622, 0x4C17, {0xB9, 0x35, 0xDA, 0xE0, 0x7E, 0xE9, 0x28, 0x9C}}; +static const GUID_T asf_guid_adv_content_encryption = {0x43058533, 0x6981, 0x49E6, {0x9B, 0x74, 0xAD, 0x12, 0xCB, 0x86, 0xD5, 0x8C}}; +static const GUID_T asf_guid_compatibility = {0x26F18B5D, 0x4584, 0x47EC, {0x9F, 0x5F, 0x0E, 0x65, 0x1F, 0x04, 0x52, 0xC9}}; +static const GUID_T asf_guid_script_command = {0x1EFB1A30, 0x0B62, 0x11D0, {0xA3, 0x9B, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}; +static const GUID_T asf_guid_mutual_exclusion = {0xD6E229DC, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; + +static const GUID_T asf_guid_stream_type_video = {0xBC19EFC0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; +static const GUID_T asf_guid_stream_type_audio = {0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; + +/****************************************************************************** +List of GUIDs and their associated processing functions +******************************************************************************/ +static struct { + const GUID_T *guid; + const char *psz_name; + VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T *, int64_t ); + +} asf_object_list[] = +{ + {&asf_guid_header, "header", asf_read_object_header}, + {&asf_guid_file_props, "file properties", asf_read_object_file_properties}, + {&asf_guid_stream_props, "stream properties", asf_read_object_stream_properties}, + {&asf_guid_ext_stream_props, "extended stream properties", asf_read_object_ext_stream_properties}, + {&asf_guid_data, "data", asf_read_object_data}, + {&asf_guid_simple_index, "simple index", asf_read_object_simple_index}, + {&asf_guid_index, "index", asf_read_object_index}, + {&asf_guid_index_parameters, "index parameters", asf_read_object_index_parameters}, + {&asf_guid_header_ext, "header extension", asf_read_object_header_ext}, + {&asf_guid_codec_list, "codec list", asf_read_object_codec_list}, + {&asf_guid_content_description, "content description", asf_read_object_content_description}, + {&asf_guid_ext_content_description, "extended content description", asf_skip_unprocessed_object}, + {&asf_guid_stream_bitrate_props, "stream bitrate properties", asf_read_object_stream_bitrate_props}, + {&asf_guid_language_list, "language list", asf_skip_unprocessed_object}, + {&asf_guid_metadata, "metadata", asf_skip_unprocessed_object}, + {&asf_guid_padding, "padding", asf_skip_unprocessed_object}, + {&asf_guid_compatibility, "compatibility", asf_skip_unprocessed_object}, + {&asf_guid_script_command, "script command", asf_skip_unprocessed_object}, + {&asf_guid_mutual_exclusion, "mutual exclusion", asf_skip_unprocessed_object}, + {&asf_guid_content_encryption, "content encryption", &asf_read_object_content_encryption}, + {&asf_guid_ext_content_encryption, "extended content encryption", &asf_read_object_ext_content_encryption}, + {&asf_guid_adv_content_encryption, "advanced content encryption", &asf_read_object_adv_content_encryption}, + {0, "unknown", asf_skip_unprocessed_object} +}; + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/** Find the track associated with an ASF stream id */ +static VC_CONTAINER_TRACK_T *asf_reader_find_track( VC_CONTAINER_T *p_ctx, unsigned int stream_id, + bool b_create) +{ + VC_CONTAINER_TRACK_T *p_track = 0; + VC_CONTAINER_MODULE_T * module = p_ctx->priv->module; + unsigned int i; + + /* discard the key-frame flag */ + stream_id &= 0x7f; + + /* look to see if we have already allocated the stream */ + i = module->stream_number_to_index[stream_id]; + + if(i < p_ctx->tracks_num) /* We found it */ + p_track = p_ctx->tracks[i]; + + if(!p_track && b_create && p_ctx->tracks_num < ASF_TRACKS_MAX) + { + /* Allocate and initialise a new track */ + p_ctx->tracks[p_ctx->tracks_num] = p_track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(p_track) + { + /* store the stream ID */ + p_track->priv->module->stream_id = stream_id; + + /* Store the translation table value */ + module->stream_number_to_index[stream_id] = p_ctx->tracks_num; + + /* count the track */ + p_ctx->tracks_num++; + } + } + + if(!p_track && b_create) + LOG_DEBUG(p_ctx, "could not create track for stream id: %i", stream_id); + + return p_track; +} + +/** Base function used to read an ASF object from the ASF header. + * This will read the object header do lots of sanity checking and pass on the rest + * of the reading to the object specific reading function */ +static VC_CONTAINER_STATUS_T asf_read_object( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t object_size, offset = STREAM_POSITION(p_ctx); + unsigned int i, unknown_objects = 0, is_data_object; + GUID_T guid; + + /* Sanity check the size of the data */ + if(size && size < ASF_OBJECT_HEADER_SIZE) + { + LOG_DEBUG(p_ctx, "invalid object header (too small)"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if(READ_GUID(p_ctx, &guid, "Object ID") != sizeof(guid)) + return STREAM_STATUS(p_ctx); + + /* Find out which GUID we are dealing with */ + for( i = 0; asf_object_list[i].guid; i++ ) + { + if(guid.word0 != asf_object_list[i].guid->word0) continue; + if(!memcmp(&guid, asf_object_list[i].guid, sizeof(guid))) break; + } + + LOG_FORMAT(p_ctx, "Object Name: %s", asf_object_list[i].psz_name); + + /* Bail out if we find too many consecutive unknown objects */ + if(!asf_object_list[i].guid) unknown_objects++; + else unknown_objects = 0; + if(unknown_objects >= ASF_MAX_CONSECUTIVE_UNKNOWN_OBJECTS) + { + LOG_DEBUG(p_ctx, "too many unknown objects"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + is_data_object = asf_object_list[i].pf_func == asf_read_object_data; + + object_size = READ_U64(p_ctx, "Object Size"); + + /* Sanity check the object size */ + if(object_size < 0 /* Shouldn't ever get that big */ || + /* Minimum size check (data object can have a size == 0) */ + (object_size < ASF_OBJECT_HEADER_SIZE && !(is_data_object && !object_size)) || + /* Only the data object can really be massive */ + (!is_data_object && object_size > ASF_MAX_OBJECT_SIZE)) + { + LOG_DEBUG(p_ctx, "object %s has an invalid size (%"PRIi64")", + asf_object_list[i].psz_name, object_size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + if(size && object_size > size) + { + LOG_DEBUG(p_ctx, "object %s is bigger than it should (%"PRIi64" > %"PRIi64")", + asf_object_list[i].psz_name, object_size, size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + size = object_size; + + if(module->object_level >= 2 * ASF_MAX_OBJECT_LEVEL) + { + LOG_DEBUG(p_ctx, "object %s is too deep. skipping", asf_object_list[i].psz_name); + status = asf_skip_unprocessed_object(p_ctx, size - ASF_OBJECT_HEADER_SIZE); + /* Just bail out, hoping we have enough data */ + } + else + { + module->object_level++; + + /* Call the object specific parsing function */ + status = asf_object_list[i].pf_func(p_ctx, size - ASF_OBJECT_HEADER_SIZE); + + module->object_level--; + + if(status != VC_CONTAINER_SUCCESS) + LOG_DEBUG(p_ctx, "object %s appears to be corrupted (%i)", asf_object_list[i].psz_name, status); + } + + /* The stream position should be exactly at the end of the object */ + { + int64_t bytes_processed = STREAM_POSITION(p_ctx) - offset; + + /* fail with overruns */ + if (bytes_processed > size) + { + /* Things have gone really bad here and we ended up reading past the end of the + * object. We could maybe try to be clever and recover by seeking back to the end + * of the object. However if we get there, the file is clearly corrupted so there's + * no guarantee it would work anyway. */ + LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of object %s", + bytes_processed-size, asf_object_list[i].psz_name); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* Handle underruns by throwing away the data (this should never happen, but we don't really care if it does) */ + if (bytes_processed < size) + { + size -= bytes_processed; + LOG_DEBUG(p_ctx, "%"PRIi64" bytes left unread in object %s", size, asf_object_list[i].psz_name); + + if(size < ASF_MAX_OBJECT_SIZE) + SKIP_BYTES(p_ctx, size); /* read a small amount */ + else + SEEK(p_ctx, STREAM_POSITION(p_ctx) + size); /* seek a large distance */ + } + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF header object */ +static VC_CONTAINER_STATUS_T asf_read_object_header( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset = STREAM_POSITION(p_ctx); + + /* Sanity check the size of the data */ + if((size -= 6) < 0) return VC_CONTAINER_ERROR_CORRUPTED; + + SKIP_U32(p_ctx, "Number of Header Objects"); /* FIXME: could use that */ + SKIP_U8(p_ctx, "Reserved1"); + SKIP_U8(p_ctx, "Reserved2"); + + /* Read contained objects */ + module->object_level++; + while(status == VC_CONTAINER_SUCCESS && size >= ASF_OBJECT_HEADER_SIZE) + { + offset = STREAM_POSITION(p_ctx); + status = asf_read_object(p_ctx, size); + size -= (STREAM_POSITION(p_ctx) - offset); + } + module->object_level--; + + return status; +} + +/** Reads an ASF extended header object */ +static VC_CONTAINER_STATUS_T asf_read_object_header_ext( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t data_size, offset; + + ASF_SKIP_GUID(p_ctx, size, "Reserved Field 1"); + ASF_SKIP_U16(p_ctx, size, "Reserved Field 2"); + data_size = ASF_READ_U32(p_ctx, size, "Header Extension Data Size"); + + if(data_size != size) + LOG_DEBUG(p_ctx, "invalid header extension data size (%"PRIi64",%"PRIi64")", data_size, size); + + CHECK_POINT(p_ctx, size); + + /* Read contained objects */ + module->object_level++; + while(status == VC_CONTAINER_SUCCESS && size >= ASF_OBJECT_HEADER_SIZE) + { + offset = STREAM_POSITION(p_ctx); + status = asf_read_object(p_ctx, size); + size -= (STREAM_POSITION(p_ctx) - offset); + } + module->object_level--; + + return status; +} + +/** Reads an ASF file properties object */ +static VC_CONTAINER_STATUS_T asf_read_object_file_properties( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t max_packet_size; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + ASF_SKIP_GUID(p_ctx, size, "File ID"); + ASF_SKIP_U64(p_ctx, size, "File Size"); + ASF_SKIP_U64(p_ctx, size, "Creation Date"); + ASF_SKIP_U64(p_ctx, size, "Data Packets Count"); + module->duration = ASF_READ_U64(p_ctx, size, "Play Duration") / UINT64_C(10); /* read in 100nS units, stored in uS */ + ASF_SKIP_U64(p_ctx, size, "Send Duration"); + module->preroll = ASF_READ_U64(p_ctx, size, "Preroll") * UINT64_C(1000); /* read in mS, storedin uS */ + module->broadcast = ASF_READ_U32(p_ctx, size, "Flags") & 0x1; + module->packet_size = ASF_READ_U32(p_ctx, size, "Minimum Data Packet Size"); + max_packet_size = ASF_READ_U32(p_ctx, size, "Maximum Data Packet Size"); + ASF_SKIP_U32(p_ctx, size, "Maximum Bitrate"); + + if(module->preroll < module->duration) module->duration -= module->preroll; + else module->duration = 0; + + /* Sanity check the packet size */ + if(!module->packet_size) + { + LOG_DEBUG(p_ctx, "packet size cannot be 0"); + return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED; + } + + if(max_packet_size != module->packet_size) + { + LOG_DEBUG(p_ctx, "asf stream not supported (min packet size: %i != max packet size: %i)", + module->packet_size, max_packet_size); + return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED; + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads the bitmapinfoheader structure contained in a stream properties object */ +static VC_CONTAINER_STATUS_T asf_read_bitmapinfoheader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *p_track, int64_t size ) +{ + uint32_t bmih_size, formatdata_size; + uint32_t fourcc; + + /* Sanity check the size of the data */ + if(size < 40 + 11) return VC_CONTAINER_ERROR_CORRUPTED; + + /* Read the preamble to the BITMAPINFOHEADER */ + ASF_SKIP_U32(p_ctx, size, "Encoded Image Width"); + ASF_SKIP_U32(p_ctx, size, "Encoded Image Height"); + ASF_SKIP_U8(p_ctx, size, "Reserved Flags"); + formatdata_size = ASF_READ_U16(p_ctx, size, "Format Data Size"); + + /* Sanity check the size of the data */ + if(formatdata_size < 40 || size < formatdata_size) return VC_CONTAINER_ERROR_CORRUPTED; + bmih_size = ASF_READ_U32(p_ctx, size, "Format Data Size"); + if(bmih_size < 40 || bmih_size > formatdata_size) return VC_CONTAINER_ERROR_CORRUPTED; + + /* Read BITMAPINFOHEADER structure */ + p_track->format->type->video.width = ASF_READ_U32(p_ctx, size, "Image Width"); + p_track->format->type->video.height = ASF_READ_U32(p_ctx, size, "Image Height"); /* Signed */ + ASF_SKIP_U16(p_ctx, size, "Reserved"); + ASF_SKIP_U16(p_ctx, size, "Bits Per Pixel Count"); + ASF_READ_BYTES(p_ctx, size, (char *)&fourcc, 4); /* Compression ID */ + LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc); + p_track->format->codec = vfw_fourcc_to_codec(fourcc); + if(p_track->format->codec == VC_CONTAINER_CODEC_UNKNOWN) + p_track->format->codec = fourcc; + ASF_SKIP_U32(p_ctx, size, "Image Size"); + ASF_SKIP_U32(p_ctx, size, "Horizontal Pixels Per Meter"); + ASF_SKIP_U32(p_ctx, size, "Vertical Pixels Per Meter"); + ASF_SKIP_U32(p_ctx, size, "Colors Used Count"); + ASF_SKIP_U32(p_ctx, size, "Important Colors Count"); + + if(!(bmih_size -= 40))return VC_CONTAINER_SUCCESS; + + if(bmih_size > ASF_EXTRADATA_MAX) + { + LOG_DEBUG(p_ctx, "extradata truncated"); + bmih_size = ASF_EXTRADATA_MAX; + } + p_track->format->extradata = p_track->priv->module->extradata; + p_track->format->extradata_size = ASF_READ_BYTES(p_ctx, size, p_track->format->extradata, bmih_size); + + return STREAM_STATUS(p_ctx); +} + +/** Reads the waveformatex structure contained in a stream properties object */ +static VC_CONTAINER_STATUS_T asf_read_waveformatex( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *p_track, int64_t size) +{ + uint16_t extradata_size; + + /* Read WAVEFORMATEX structure */ + p_track->format->codec = waveformat_to_codec(ASF_READ_U16(p_ctx, size, "Codec ID")); + p_track->format->type->audio.channels = ASF_READ_U16(p_ctx, size, "Number of Channels"); + p_track->format->type->audio.sample_rate = ASF_READ_U32(p_ctx, size, "Samples per Second"); + p_track->format->bitrate = ASF_READ_U32(p_ctx, size, "Average Number of Bytes Per Second") * 8; + p_track->format->type->audio.block_align = ASF_READ_U16(p_ctx, size, "Block Alignment"); + p_track->format->type->audio.bits_per_sample = ASF_READ_U16(p_ctx, size, "Bits Per Sample"); + extradata_size = ASF_READ_U16(p_ctx, size, "Codec Specific Data Size"); + + CHECK_POINT(p_ctx, size); + + if(!extradata_size) return VC_CONTAINER_SUCCESS; + + /* Sanity check the size of the data */ + if(extradata_size > size) return VC_CONTAINER_ERROR_CORRUPTED; + + if(extradata_size > ASF_EXTRADATA_MAX) + { + LOG_DEBUG(p_ctx, "extradata truncated"); + extradata_size = ASF_EXTRADATA_MAX; + } + p_track->format->extradata = p_track->priv->module->extradata; + p_track->format->extradata_size = ASF_READ_BYTES(p_ctx, size, p_track->format->extradata, extradata_size); + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF stream properties object */ +static VC_CONTAINER_STATUS_T asf_read_object_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *p_track; + unsigned int ts_length, flags; + VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN; + GUID_T stream_type; + int64_t offset; + + ASF_READ_GUID(p_ctx, size, &stream_type, "Stream Type"); + ASF_SKIP_GUID(p_ctx, size, "Error Correction Type"); + + /* The time_offset field is in 100nS units. Scale back to uS */ + module->time_offset = ASF_READ_U64(p_ctx, size, "Time Offset") / UINT64_C(10); + ts_length = ASF_READ_U32(p_ctx, size, "Type-Specific Data Length"); + ASF_SKIP_U32(p_ctx, size, "Error Correction Data Length"); + flags = ASF_READ_U16(p_ctx, size, "Flags"); + ASF_SKIP_U32(p_ctx, size, "Reserved"); + + CHECK_POINT(p_ctx, size); + + /* Zero is not a valid stream id */ + if(!(flags & 0x7F)) goto skip; + + if(!memcmp(&stream_type, &asf_guid_stream_type_video, sizeof(GUID_T))) + type = VC_CONTAINER_ES_TYPE_VIDEO; + else if(!memcmp(&stream_type, &asf_guid_stream_type_audio, sizeof(GUID_T))) + type = VC_CONTAINER_ES_TYPE_AUDIO; + + /* Check we know what to do with this track */ + if(type == VC_CONTAINER_ES_TYPE_UNKNOWN) goto skip; + + /* Sanity check sizes */ + if(ts_length > size) return VC_CONTAINER_ERROR_CORRUPTED; + + p_track = asf_reader_find_track( p_ctx, flags, true); + if(!p_track) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + p_track->format->es_type = type; + + offset = STREAM_POSITION(p_ctx); + if(type == VC_CONTAINER_ES_TYPE_AUDIO) + status = asf_read_waveformatex(p_ctx, p_track, (int64_t)ts_length); + else if(type == VC_CONTAINER_ES_TYPE_VIDEO) + status = asf_read_bitmapinfoheader(p_ctx, p_track, (int64_t)ts_length); + size -= STREAM_POSITION(p_ctx) - offset; + + if(status) return status; + + p_track->priv->module->b_valid = true; + p_track->is_enabled = true; + p_track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + + /* Codec specific work-arounds */ + switch(p_track->format->codec) + { + case VC_CONTAINER_CODEC_MPGA: + /* Can't guarantee that the data is framed */ + p_track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + break; + default: break; + } + + skip: + if(size) SKIP_BYTES(p_ctx, size); + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF extended stream properties object */ +static VC_CONTAINER_STATUS_T asf_read_object_ext_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_T *p_track; + unsigned int i, name_count, pes_count, length, stream_id; + + ASF_SKIP_U64(p_ctx, size, "Start Time"); + ASF_SKIP_U64(p_ctx, size, "End Time"); + ASF_SKIP_U32(p_ctx, size, "Data Bitrate"); + ASF_SKIP_U32(p_ctx, size, "Buffer Size"); + ASF_SKIP_U32(p_ctx, size, "Initial Buffer Fullness"); + ASF_SKIP_U32(p_ctx, size, "Alternate Data Bitrate"); + ASF_SKIP_U32(p_ctx, size, "Alternate Buffer Size"); + ASF_SKIP_U32(p_ctx, size, "Alternate Initial Buffer Fullness"); + ASF_SKIP_U32(p_ctx, size, "Maximum Object Size"); + ASF_SKIP_U32(p_ctx, size, "Flags"); + stream_id = ASF_READ_U16(p_ctx, size, "Stream Number"); + ASF_SKIP_U16(p_ctx, size, "Stream Language ID Index"); + ASF_SKIP_U64(p_ctx, size, "Average Time Per Frame"); + name_count = ASF_READ_U16(p_ctx, size, "Stream Name Count"); + pes_count = ASF_READ_U16(p_ctx, size, "Payload Extension System Count"); + + CHECK_POINT(p_ctx, size); + + p_track = asf_reader_find_track( p_ctx, stream_id, true); + if(!p_track) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + /* Stream Names */ + for(i = 0; i < name_count; i++) + { + if(size < 4) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_U16(p_ctx, size, "Language ID Index"); + length = ASF_READ_U16(p_ctx, size, "Stream Name Length"); + if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_BYTES(p_ctx, size, length); /* Stream Name */ + } + + CHECK_POINT(p_ctx, size); + + /* Payload Extension Systems */ + for(i = 0; i < pes_count; i++) + { + if(size < 22) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_GUID(p_ctx, size, "Extension System ID"); + ASF_SKIP_U16(p_ctx, size, "Extension Data Size"); + length = ASF_READ_U32(p_ctx, size, "Extension System Info Length"); + if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_BYTES(p_ctx, size, length); /* Extension System Info */ + } + + CHECK_POINT(p_ctx, size); + + /* Optional Stream Properties Object */ + if(size >= ASF_OBJECT_HEADER_SIZE) + status = asf_read_object(p_ctx, size); + + return status; +} + +/** Reads an ASF simple index object */ +static VC_CONTAINER_STATUS_T asf_read_object_simple_index( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = 0; + uint64_t time_interval, index_duration; + uint32_t count; + unsigned int i; + + ASF_SKIP_GUID(p_ctx, size, "File ID"); + + /* time in 100nS units, converted to uS */ + time_interval = ASF_READ_U64(p_ctx, size, "Index Entry Time Interval") / UINT64_C(10); + ASF_SKIP_U32(p_ctx, size, "Maximum Packet Count"); + count = ASF_READ_U32(p_ctx, size, "Index Entries Count"); + + CHECK_POINT(p_ctx, size); + + if(count > size / 6) + { + LOG_DEBUG(p_ctx, "invalid number of entries in the index (%i, %"PRIi64")", count, size / 6); + count = (uint32_t)(size / 6); + } + + /* Find the track corresponding to this index */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + if(p_ctx->tracks[i]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) continue; + if(p_ctx->tracks[i]->priv->module->simple_index.offset) continue; + break; + } + + /* Skip the index if we can't find the associated track */ + if(i == p_ctx->tracks_num || !count || !time_interval) return VC_CONTAINER_SUCCESS; + track_module = p_ctx->tracks[i]->priv->module; + + track_module->simple_index.offset = STREAM_POSITION(p_ctx); + track_module->simple_index.time_interval = time_interval; + track_module->simple_index.num_entries = count; + + /* Check that the index covers the whole duration of the stream */ + index_duration = (count * time_interval); + if(module->preroll + module->time_offset < index_duration) + index_duration -= module->preroll + module->time_offset; + else + index_duration = 0; + + if((uint64_t)module->duration > index_duration + time_interval) + { + track_module->simple_index.incomplete = true; + } + + LOG_DEBUG(p_ctx, "index covers %fS on %fS", + (float)index_duration / 1E6, (float)module->duration / 1E6); + +#if defined(ENABLE_CONTAINERS_LOG_FORMAT) && defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE) + for(i = 0; i < count; i++) + { + LOG_FORMAT(p_ctx, "Entry: %u", i); + ASF_SKIP_U32(p_ctx, size, "Packet Number"); + ASF_SKIP_U16(p_ctx, size, "Packet Count"); + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break; + } + size = i * 6; +#else + size = CACHE_BYTES(p_ctx, count * 6); +#endif + + /* Check that the index is complete */ + if(size / 6 != count ) + { + LOG_DEBUG(p_ctx, "index is incomplete (%i entries on %i)", (int)size / 6, count); + track_module->simple_index.num_entries = (uint32_t)(size / 6); + track_module->simple_index.incomplete = true; + } + + /* If we haven't had an index before, or this track is enabled, we'll store this one. + * (Usually there will only be one video track, and it will be enabled, so both tests + * will pass. This check is an attempt to handle content not structured as it should be) */ + if ((!module->simple_index_track) || (p_ctx->tracks[i]->is_enabled)) + { + /* Save the track so we don't have to look for it in when seeking */ + module->simple_index_track = track_module; + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF index object */ +static VC_CONTAINER_STATUS_T asf_read_object_index( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t i, specifiers_count, blocks_count; + uint32_t best_specifier_type[ASF_TRACKS_MAX] = {0}; + + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + /* Read the time interval and scale to microseconds */ + module->top_level_index.entry_time_interval + = (uint64_t)ASF_READ_U32(p_ctx, size, "Index Entry Time Interval") * INT64_C(1000); + + module->top_level_index.specifiers_count + = specifiers_count + = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Specifiers Count"); + + module->top_level_index.block_count + = blocks_count + = ASF_READ_U32(p_ctx, size, "Index Blocks Count"); + + CHECK_POINT(p_ctx, size); + + /* Index specifiers. Search for the one we like best */ + if(size < specifiers_count * 4) return VC_CONTAINER_ERROR_CORRUPTED; + for(i = 0; i < specifiers_count; i++) + { + uint32_t stream_id = (uint32_t)ASF_READ_U16(p_ctx, size, "Stream Number"); + uint32_t index_type = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Type"); + + /* Find the track index for this stream */ + unsigned track = module->stream_number_to_index[stream_id]; + + if ((track < ASF_TRACKS_MAX) && + (index_type > best_specifier_type[track])) + { + /* We like this better than any we have seen before. Note - if we don't like any + * the file must be subtly corrupt - best we say nothing, and attempt a seek with + * the data for the first specifier, it will be better than nothing. At worst it + * will play until a seek is attempted */ + module->top_level_index.active_specifiers[track] = i; + best_specifier_type[track] = index_type; + } + } + + for (i = 0; i < p_ctx->tracks_num; i++) + { + LOG_DEBUG(p_ctx, "indexing track %"PRIu32" with specifier %"PRIu32, + i, module->top_level_index.active_specifiers[i]); + } + + CHECK_POINT(p_ctx, size); + + /* The blocks start here */ + module->top_level_index.blocks_offset = STREAM_POSITION(p_ctx); + + /* Index blocks */ +#if !(defined(ENABLE_CONTAINERS_LOG_FORMAT) && defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE)) + blocks_count = 0; /* Don't log the index. Note we'll get a warning on unprocessed data */ +#endif + /* coverity[dead_error_condition] Code needs to stay there for debugging purposes */ + for(i = 0; i < blocks_count; i++) + { + uint32_t j, k, count = ASF_READ_U32(p_ctx, size, "Index Entry Count"); + + for(j = 0; j < specifiers_count; j++) + { + ASF_SKIP_U64(p_ctx, size, "Block Positions"); + } + for(j = 0; j < count; j++) + { + for(k = 0; k < specifiers_count; k++) + { + ASF_SKIP_U32(p_ctx, size, "Offsets"); + } + } + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF index parameters object */ +static VC_CONTAINER_STATUS_T asf_read_object_index_parameters( VC_CONTAINER_T *p_ctx, int64_t size ) +{ +#ifdef ENABLE_CONTAINERS_LOG_FORMAT + /* This is added for debugging only. The spec (section 4.9) states that this is also enclosed in + * the index object (see above) and that they are identical. I think they aren't always... */ + uint32_t i, specifiers_count; + + /* Read the time interval in milliseconds */ + ASF_SKIP_U32(p_ctx, size, "Index Entry Time Interval"); + + specifiers_count = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Specifiers Count"); + + CHECK_POINT(p_ctx, size); + + /* Index specifiers. Search for the one we like best */ + if(size < specifiers_count * 4) return VC_CONTAINER_ERROR_CORRUPTED; + for(i = 0; i < specifiers_count; i++) + { + ASF_SKIP_U16(p_ctx, size, "Stream Number"); + ASF_SKIP_U16(p_ctx, size, "Index Type"); + } +#endif + CHECK_POINT(p_ctx, size); + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF codec list object */ +static VC_CONTAINER_STATUS_T asf_read_object_codec_list( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t i, count, length; + + ASF_SKIP_GUID(p_ctx, size, "Reserved"); + count = ASF_READ_U32(p_ctx, size, "Codec Entries Count"); + + CHECK_POINT(p_ctx, size); + + /* Codec entries */ + for(i = 0; i < count; i++) + { + ASF_SKIP_U16(p_ctx, size, "Type"); + length = ASF_READ_U16(p_ctx, size, "Codec Name Length"); + if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, length * 2, "Codec Name"); + length = ASF_READ_U16(p_ctx, size, "Codec Description Length"); + if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, length * 2, "Codec Description"); + length = ASF_READ_U16(p_ctx, size, "Codec Information Length"); + if(size < length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_BYTES(p_ctx, size, length); + + CHECK_POINT(p_ctx, size); + } + + return status; +} + +/** Reads an ASF content description object */ +static VC_CONTAINER_STATUS_T asf_read_object_content_description( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint16_t t_length, a_length, c_length, d_length, r_length; + + t_length = ASF_READ_U16(p_ctx, size, "Title Length"); + a_length = ASF_READ_U16(p_ctx, size, "Author Length"); + c_length = ASF_READ_U16(p_ctx, size, "Copyright Length"); + d_length = ASF_READ_U16(p_ctx, size, "Description Length"); + r_length = ASF_READ_U16(p_ctx, size, "Rating Length"); + + CHECK_POINT(p_ctx, size); + + if(size < t_length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, t_length, "Title"); + if(size < a_length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, a_length, "Author"); + if(size < c_length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, c_length, "Copyright"); + if(size < d_length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, d_length, "Description"); + if(size < r_length) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_STRING(p_ctx, size, r_length, "Rating"); + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF stream bitrate properties object */ +static VC_CONTAINER_STATUS_T asf_read_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint16_t i, count; + + count = ASF_READ_U16(p_ctx, size, "Bitrate Records Count"); + + /* Bitrate records */ + if(size < count * 6) return VC_CONTAINER_ERROR_CORRUPTED; + for(i = 0; i < count; i++) + { + ASF_SKIP_U16(p_ctx, size, "Flags"); + ASF_SKIP_U32(p_ctx, size, "Average Bitrate"); + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF content encryption object */ +static VC_CONTAINER_STATUS_T asf_read_object_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t length; + + length = ASF_READ_U32(p_ctx, size, "Secret Data Length"); + ASF_SKIP_BYTES(p_ctx, size, length); + + length = ASF_READ_U32(p_ctx, size, "Protection Type Length"); + ASF_SKIP_BYTES(p_ctx, size, length); + + length = ASF_READ_U32(p_ctx, size, "Key ID Length"); + ASF_SKIP_BYTES(p_ctx, size, length); + + length = ASF_READ_U32(p_ctx, size, "License URL Length"); + ASF_SKIP_BYTES(p_ctx, size, length); /* null-terminated ASCII string */ + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF extended content encryption object */ +static VC_CONTAINER_STATUS_T asf_read_object_ext_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t length; + + length = ASF_READ_U32(p_ctx, size, "Data Size"); + ASF_SKIP_BYTES(p_ctx, size, length); + + return STREAM_STATUS(p_ctx); +} + +/** Reads an ASF advanced content encryption object */ +static VC_CONTAINER_STATUS_T asf_read_object_adv_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t i, count; + + count = ASF_READ_U16(p_ctx, size, "Content Encryption Records Count"); + + for(i = 0; i < count; i++) + { + uint32_t j, rec_count, data_size, length; + + ASF_SKIP_GUID(p_ctx, size, "System ID"); + ASF_SKIP_U32(p_ctx, size, "System Version"); + rec_count = ASF_READ_U16(p_ctx, size, "Encrypted Object Record Count"); + + CHECK_POINT(p_ctx, size); + + for(j = 0; j < rec_count; j++) + { + ASF_SKIP_U16(p_ctx, size, "Encrypted Object ID Type"); + length = ASF_READ_U16(p_ctx, size, "Encrypted Object ID Length"); + if(length > size) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_BYTES(p_ctx, size, length); + CHECK_POINT(p_ctx, size); + } + + data_size = ASF_READ_U32(p_ctx, size, "Data Size"); + if(data_size > size) return VC_CONTAINER_ERROR_CORRUPTED; + ASF_SKIP_BYTES(p_ctx, size, data_size); + CHECK_POINT(p_ctx, size); + } + + return STREAM_STATUS(p_ctx); +} + +/** Skips over an object that is if a type we don't handle, or is nested too deep */ +static VC_CONTAINER_STATUS_T asf_skip_unprocessed_object( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + LOG_DEBUG(p_ctx, "%"PRIi64" bytes ignored in unhandled object", size); + + if(size < ASF_MAX_OBJECT_SIZE) + SKIP_BYTES(p_ctx, size); /* read a small amount */ + else + SEEK(p_ctx, STREAM_POSITION(p_ctx) + size); /* seek a large distance */ + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_find_packet_header( VC_CONTAINER_T *p_ctx, + ASF_PACKET_STATE *p_state ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int search_size = 64*1024; /* should be max packet size according to spec */ +#ifdef ENABLE_CONTAINER_LOG_DEBUG + uint64_t offset = STREAM_POSITION(p_ctx); +#endif + uint8_t h[3]; + VC_CONTAINER_PARAM_UNUSED(p_state); + + /* Limit the search up to what should theoretically be the packet boundary */ + if(module->packet_size) + search_size = module->packet_size - + (STREAM_POSITION(p_ctx) - module->data_offset) % module->packet_size; + + for(; search_size > sizeof(h); search_size--) + { + if(PEEK_BYTES(p_ctx, h, sizeof(h)) != sizeof(h)) + return STREAM_STATUS(p_ctx); + + if(!h[0] && !h[1] && h[2] == 0x82) + { + search_size = 2; + break; /* Got it */ + } + + SKIP_BYTES(p_ctx, 1); + } + + /* If we failed, we just skip to the theoretical packet boundary */ + SKIP_BYTES(p_ctx, search_size); + + LOG_DEBUG(p_ctx, "found potential sync, discarded %"PRIu64" bytes)", + STREAM_POSITION(p_ctx) - offset); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_read_packet_header( VC_CONTAINER_T *p_ctx, + ASF_PACKET_STATE *p_state, uint64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint64_t offset = STREAM_POSITION(p_ctx); + uint8_t flags, property_flags, length; + VC_CONTAINER_PARAM_UNUSED(size); + + p_state->start = offset; + + LOG_FORMAT(p_ctx, "Packet Offset: %"PRIu64, offset); + + if ((module->data_size > 0) && (offset >= (module->data_size + module->data_offset))) + { + return VC_CONTAINER_ERROR_EOS; + } + + /* Find out whether we are dealing with error correction data or payload parsing info */ + if( PEEK_U8(p_ctx) >> 7 ) + { + /* We have error correction data */ + flags = READ_U8(p_ctx, "Error Correction Flags"); + length = flags & 0xF; + SKIP_BYTES(p_ctx, length); /* Error correction data */ + } + + /* Payload parsing information */ + flags = READ_U8(p_ctx, "Length Type Flags"); + p_state->multiple_payloads = flags & 1; + property_flags = READ_U8(p_ctx, "Property Flags"); + p_state->replicated_data_lt = (property_flags >> 0) & 0x3; + p_state->offset_into_media_object_lt = (property_flags >> 2) & 0x3; + p_state->media_object_number_lt = (property_flags >> 4) & 0x3; + + /* Sanity check stream number length type */ + if(((property_flags >> 6) & 0x3) != 1) + goto error; + + /* If there's no packet size field we default to the size in the file header. */ + p_state->size = READ_VLC(p_ctx, (flags >> 5) & 0x3 /* Packet length type */, + module->packet_size, "Packet Length"); + + READ_VLC(p_ctx, (flags>>1)&0x3 /* Sequence type */, 0, "Sequence"); + p_state->padding_size = READ_VLC(p_ctx, (flags>>3)&0x3 /* Padding length type */, 0, "Padding Length"); + p_state->send_time = READ_U32(p_ctx, "Send Time") * UINT64_C(1000); /* Read in millisecond units, stored in uS */ + SKIP_U16(p_ctx, "Duration"); /* in milliseconds */ + + p_state->num_payloads = 1; + p_state->current_payload = 0; + if(p_state->multiple_payloads) + { + LOG_FORMAT(p_ctx, "Multiple Payloads"); + flags = READ_U8(p_ctx, "Payload Flags"); + p_state->num_payloads = flags & 0x3F; + LOG_FORMAT(p_ctx, "Number of Payloads: %i", p_state->num_payloads); + p_state->payload_lt = (flags >> 6) & 3; + + /* Sanity check */ + if(!p_state->num_payloads) goto error; + } + + /* Update the current offset in the packet. */ + p_state->current_offset = STREAM_POSITION(p_ctx) - offset; + + /* Sanity check offset */ + if(p_state->current_offset > p_state->size) goto error; + + /* Sanity check padding size */ + if(p_state->padding_size + p_state->current_offset > p_state->size) goto error; + + /* Sanity check packet size */ + if(!module->broadcast && + (p_state->size != module->packet_size)) goto error; + + return STREAM_STATUS(p_ctx); + + error: + LOG_FORMAT(p_ctx, "Invalid payload parsing information (offset %"PRIu64")", STREAM_POSITION(p_ctx)); + return STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS ? + VC_CONTAINER_ERROR_CORRUPTED : STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_read_payload_header( VC_CONTAINER_T *p_ctx, + ASF_PACKET_STATE *p_state /* uint64_t size */ ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t rep_data_length; + + if(p_state->current_payload >= p_state->num_payloads) + return VC_CONTAINER_ERROR_CORRUPTED; + + p_state->stream_num = READ_U8(p_ctx, "Stream Number"); + if(!(p_state->stream_num & 0x7F)) return VC_CONTAINER_ERROR_CORRUPTED; + + p_state->media_object_num = READ_VLC(p_ctx, p_state->media_object_number_lt, 0, "Media Object Number"); + + /* For a compressed packet this field is a timestamp, and is moved to p_state->media_object_pts later */ + p_state->media_object_off = READ_VLC(p_ctx, p_state->offset_into_media_object_lt, 0, "Offset Into Media Object"); + rep_data_length = READ_VLC(p_ctx, p_state->replicated_data_lt, 0, "Replicated Data Length"); + + /* Sanity check the replicated data length */ + if(rep_data_length && rep_data_length != 1 && + (rep_data_length < 8 || + STREAM_POSITION(p_ctx) - p_state->start + p_state->padding_size + rep_data_length > p_state->size)) + { + LOG_FORMAT(p_ctx, "invalid replicated data length"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* Read what we need from the replicated data */ + if(rep_data_length > 1) + { + p_state->media_object_size = READ_U32(p_ctx, "Media Object Size"); + p_state->media_object_pts = READ_U32(p_ctx, "Presentation Time") * UINT64_C(1000); + p_state->compressed_payloads = 0; + SKIP_BYTES(p_ctx, rep_data_length - 8); /* Rest of replicated data */ + } + else if(rep_data_length == 1) + { + LOG_FORMAT(p_ctx, "Compressed Payload Data"); + p_state->media_object_pts_delta = READ_U8(p_ctx, "Presentation Time Delta") * UINT64_C(1000); + p_state->compressed_payloads = 1; + + /* Move the pts from media_object_off where it was read, and adjust it */ + p_state->media_object_off *= UINT64_C(1000); + p_state->media_object_pts = p_state->media_object_off - p_state->media_object_pts_delta; + p_state->media_object_off = 0; + p_state->media_object_size = 0; + } + else + { + p_state->media_object_size = 0; + p_state->media_object_pts = p_state->send_time; + p_state->compressed_payloads = 0; + } + + if(p_state->media_object_pts > module->preroll + module->time_offset) + p_state->media_object_pts -= (module->preroll + module->time_offset); + else p_state->media_object_pts = 0; + + p_state->payload_size = p_state->size - p_state->padding_size - (STREAM_POSITION(p_ctx) - p_state->start); + if(p_state->multiple_payloads) + { + p_state->payload_size = READ_VLC(p_ctx, p_state->payload_lt, 0, "Payload Length"); + if(!p_state->payload_size) return VC_CONTAINER_ERROR_CORRUPTED; + } + else + LOG_FORMAT(p_ctx, "Payload Length: %i", p_state->payload_size); + + if(p_state->payload_size >= p_state->size) return VC_CONTAINER_ERROR_CORRUPTED; + + p_state->subpayload_size = p_state->payload_size; + + /* Update current_offset to reflect the variable number of bytes we just read */ + p_state->current_offset = STREAM_POSITION(p_ctx) - p_state->start; + + /* Sanity check offset */ + if(p_state->current_offset > p_state->size) return VC_CONTAINER_ERROR_CORRUPTED; + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_read_object_data( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + VC_CONTAINER_PARAM_UNUSED(size); + + SKIP_GUID(p_ctx, "File ID"); + SKIP_U64(p_ctx, "Total Data Packets"); + SKIP_U16(p_ctx, "Reserved"); + module->data_offset = STREAM_POSITION(p_ctx); + + /* Initialise state for all tracks */ + module->packet_state.start = module->data_offset; + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i]; + p_track->priv->module->p_packet_state = &module->packet_state; + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +/* Read the next sub-payload or next payload */ +static VC_CONTAINER_STATUS_T asf_read_next_payload_header( VC_CONTAINER_T *p_ctx, + ASF_PACKET_STATE *p_state, uint32_t *pi_track, uint32_t *pi_length) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + + if(p_state->subpayload_size) + { + /* We still haven't read the current subpayload, return the info we already have */ + goto end; + } + + /* Check if we're done reading a packet */ + if(p_state->current_payload >= p_state->num_payloads) + { + /* Skip the padding at the end */ + if(p_state->size) + { + int32_t pad_length = p_state->size - (STREAM_POSITION(p_ctx) - p_state->start); + if(pad_length < 0) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_BYTES(p_ctx, pad_length); /* Padding */ + } + + /* Read the header for the next packet */ + module->object_level = 0; /* For debugging */ + status = asf_read_packet_header( p_ctx, p_state, (uint64_t)0/*size???*/ ); + module->object_level = 1; /* For debugging */ + if(status != VC_CONTAINER_SUCCESS) return status; + } + + /* Check if we're done reading a payload */ + if(!p_state->payload_size) + { + /* Read the payload header */ + status = asf_read_payload_header( p_ctx, p_state ); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + /* For compressed payloads, payload_size != subpayload_size */ + if(p_state->compressed_payloads && p_state->payload_size) + { + p_state->payload_size--; + p_state->subpayload_size = READ_U8(p_ctx, "Sub-Payload Data Length"); + if(p_state->subpayload_size > p_state->payload_size) + { + /* TODO: do something ? */ + LOG_DEBUG(p_ctx, "subpayload is too big"); + p_state->subpayload_size = p_state->payload_size; + } + p_state->media_object_off = 0; + p_state->media_object_size = p_state->subpayload_size; + p_state->media_object_pts += p_state->media_object_pts_delta; + } + + end: + /* We've read the payload header, return the requested info */ + if(pi_track) *pi_track = module->stream_number_to_index[p_state->stream_num & 0x7F]; + if(pi_length) *pi_length = p_state->subpayload_size; + + p_state->current_offset = STREAM_POSITION(p_ctx) - p_state->start; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +/* read the next payload (not sub-payload) */ +static VC_CONTAINER_STATUS_T asf_read_next_payload( VC_CONTAINER_T *p_ctx, + ASF_PACKET_STATE *p_state, uint8_t *p_data, uint32_t *pi_size ) +{ + uint32_t subpayload_size = p_state->subpayload_size; + + if(p_data && *pi_size < subpayload_size) subpayload_size = *pi_size; + + if(!p_state->subpayload_size) + return VC_CONTAINER_SUCCESS; + + p_state->payload_size -= subpayload_size; + if(!p_state->payload_size) p_state->current_payload++; + p_state->subpayload_size -= subpayload_size; + p_state->media_object_off += subpayload_size; + + if(p_data) *pi_size = READ_BYTES(p_ctx, p_data, subpayload_size); + else *pi_size = SKIP_BYTES(p_ctx, subpayload_size); + + p_state->current_offset += subpayload_size; + + if(*pi_size!= subpayload_size) + return STREAM_STATUS(p_ctx); + + return VC_CONTAINER_SUCCESS; +} + +/****************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +/*****************************************************************************/ +/** Read data from the ASF file + +@param p_ctx Context for the file being read from + +@param p_packet Packet information. Includes data buffer and stream ID as aprropriate. + +@param flags Flags controlling the read. + May request reading only, skipping a packet or force access to a set track. + +******************************************************************************/ +static VC_CONTAINER_STATUS_T asf_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *global_module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + ASF_PACKET_STATE *p_state; + uint32_t buffer_size = 0, track, data_size; + uint64_t state_pos; + + LOG_DEBUG(p_ctx, "asf_reader_read track %"PRIu32" flags %u", p_packet->track, flags); + + /* If a specific track has been selected, we need to use the track packet state */ + if(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + { + vc_container_assert(p_packet->track < p_ctx->tracks_num); + /* The state to use is the one referred to by the track we selected */ + p_state = p_ctx->tracks[p_packet->track]->priv->module->p_packet_state; + } + else + { + /* No specific track was selected. Read the next data from the global position. */ + p_state = &global_module->packet_state; + } + + /* Stop if the stream can't be read */ + if(p_state->eos) return VC_CONTAINER_ERROR_EOS; + if(p_state->corrupted) return VC_CONTAINER_ERROR_CORRUPTED; + + /* If we aren't in the right position in the file go there now. */ + state_pos = p_state->start + p_state->current_offset; + if ((uint64_t)STREAM_POSITION(p_ctx) != state_pos) + { + LOG_DEBUG(p_ctx, "seeking from %"PRIu64" to %"PRIu64, STREAM_POSITION(p_ctx), state_pos); + SEEK(p_ctx, state_pos); + } + + /* Look at the next payload header */ + status = asf_read_next_payload_header( p_ctx, p_state, &track, &data_size ); + if((status == VC_CONTAINER_ERROR_CORRUPTED) + && (p_state->bad_packets < ASF_MAX_CONSECUTIVE_CORRUPTED_PACKETS)) + { + /* If the current packet is corrupted we will try to search for the next packet */ + uint32_t corrupted = p_state->bad_packets; + LOG_DEBUG(p_ctx, "packet offset %"PRIi64" is corrupted", p_state->start); + memset(p_state, 0, sizeof(*p_state)); + p_state->bad_packets = corrupted + 1; + + /* TODO: flag discontinuity */ + + if(asf_find_packet_header(p_ctx, p_state) == VC_CONTAINER_SUCCESS) + { + p_state->start = STREAM_POSITION(p_ctx); + return VC_CONTAINER_ERROR_CONTINUE; + } + } + if(status == VC_CONTAINER_ERROR_EOS) p_state->eos = true; + if(status == VC_CONTAINER_ERROR_CORRUPTED) p_state->corrupted = true; + if(status != VC_CONTAINER_SUCCESS) + { + return status; + } + + p_state->bad_packets = 0; + + /* bad track number or track is disabled */ + if(track >= p_ctx->tracks_num || !p_ctx->tracks[track]->is_enabled) + { + LOG_DEBUG(p_ctx, "skipping packet because track %u is invalid or disabled", track); + + /* Skip payload by reading with a null buffer */ + status = asf_read_next_payload(p_ctx, p_state, 0, &data_size ); + if(status != VC_CONTAINER_SUCCESS) return status; + return VC_CONTAINER_ERROR_CONTINUE; + } + + track_module = p_ctx->tracks[track]->priv->module; + + /* If we are reading from the global state, and the track we found is not on the global state, + * either skip the data or reconnect it to the global state */ + if ((p_state == &global_module->packet_state) && + (track_module->p_packet_state != &global_module->packet_state)) + { + uint64_t track_pos = + track_module->p_packet_state->start + + track_module->p_packet_state->current_offset; + + /* Check if the end of the current packet is beyond the track's position */ + if (track_pos > state_pos + track_module->p_packet_state->size) + { + LOG_DEBUG(p_ctx, "skipping packet from track %u as it has already been read", track); + status = asf_read_next_payload(p_ctx, p_state, 0, &data_size); + + if(status != VC_CONTAINER_SUCCESS) return status; + return VC_CONTAINER_ERROR_CONTINUE; + } + else + { + LOG_DEBUG(p_ctx, "switching track index %u location %"PRIu64" back to global state", track, track_pos); + track_module->p_packet_state = &global_module->packet_state; + + /* Update the global state to the precise position */ + global_module->packet_state = track_module->local_packet_state; + return VC_CONTAINER_ERROR_CONTINUE; + } + } + + /* If we are forcing, and the data is from a different track, skip it. + * We may need to move the track we want onto a local state. */ + if ((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + && (track != p_packet->track)) + { + track_module = p_ctx->tracks[p_packet->track]->priv->module; + + /* If the track we found is on the same state as the track we want they must both be on the global state */ + if (p_ctx->tracks[track]->priv->module->p_packet_state == p_state) + { + LOG_DEBUG(p_ctx, "switching track index %u location %"PRIu64" away from global state", p_packet->track, state_pos); + + /* Change the track we want onto a local state */ + track_module->p_packet_state = &track_module->local_packet_state; + + /* Copy the global state into the local state for the track we are forcing */ + track_module->local_packet_state = global_module->packet_state; + } + + LOG_DEBUG(p_ctx, "skipping packet from track %u while forcing %u", track, p_packet->track); + status = asf_read_next_payload(p_ctx, track_module->p_packet_state, 0, &data_size ); + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* If we arrive here either the data is from the track we are forcing, or we are not forcing + * and we haven't already read the data while forcing that track */ + + /* If skip, and no info required, skip over it and return now. */ + if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) + return asf_read_next_payload(p_ctx, p_state, 0, &data_size ); + + /* Fill-in the packet information */ + if(p_state->media_object_pts == ASF_UNKNOWN_PTS || p_state->media_object_off) + p_packet->dts = p_packet->pts = VC_CONTAINER_TIME_UNKNOWN; + else + p_packet->dts = p_packet->pts = p_state->media_object_pts; + + p_packet->flags = 0; + + if(p_state->stream_num >> 7) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + + if(!p_state->media_object_off) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + + if(p_state->media_object_size && + p_state->media_object_off + data_size >= p_state->media_object_size) + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + + if(!p_state->media_object_size) + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + + p_packet->track = track; + + p_packet->frame_size = p_state->media_object_size; + + p_packet->size = data_size; + + /* If the skip flag is set (Info must have been too) skip the data and return */ + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + /* Skip payload by reading with a null buffer */ + return asf_read_next_payload(p_ctx, p_state, 0, &data_size ); + } + else if(flags & VC_CONTAINER_READ_FLAG_INFO) + { + return VC_CONTAINER_SUCCESS; + } + + /* Read the payload data */ + buffer_size = p_packet->buffer_size; + status = asf_read_next_payload(p_ctx, p_state, p_packet->data, &buffer_size ); + if(status != VC_CONTAINER_SUCCESS) + { + /* FIXME */ + return status; + } + + p_packet->size = buffer_size; + if(buffer_size != data_size) + p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + LOG_DEBUG(p_ctx, "asf_reader_read exit %u PTS %"PRIi64" track %"PRIu32, status, p_packet->pts, track); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_reader_index_find_time( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T* track_module, int64_t time, uint32_t *packet_num, bool forward ) +{ + VC_CONTAINER_STATUS_T status; + uint32_t entry, previous_packet_num; + bool eos = false; + + /* Default to beginning of file in case of error */ + *packet_num = 0; + + /* Special case - time zero is beginning of file */ + if(time == 0) {return VC_CONTAINER_SUCCESS;} + + /* Sanity checking */ + if(!track_module->simple_index.num_entries) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + if(!track_module->simple_index.time_interval) return VC_CONTAINER_ERROR_CORRUPTED; + + entry = time / track_module->simple_index.time_interval; + LOG_DEBUG(p_ctx, "entry: %i, offset: %"PRIi64", interv: %"PRIi64, entry, + track_module->simple_index.offset, track_module->simple_index.time_interval); + if(entry >= track_module->simple_index.num_entries) + { + entry = track_module->simple_index.num_entries - 1; + eos = true; + } + + /* Fetch the entry from the index */ + status = SEEK(p_ctx, track_module->simple_index.offset + 6 * entry); + if(status != VC_CONTAINER_SUCCESS) return status; + *packet_num = READ_U32(p_ctx, "Packet Number"); + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return STREAM_STATUS(p_ctx); + + /* When asking for the following keyframe we need to find the next entry with a greater + * packet number */ + previous_packet_num = *packet_num; + while(!eos && forward && previous_packet_num == *packet_num) + { + if(++entry == track_module->simple_index.num_entries) {eos = true; break;} + status = SEEK(p_ctx, track_module->simple_index.offset + 6 * entry); + if(status != VC_CONTAINER_SUCCESS) break; + *packet_num = READ_U32(p_ctx, "Packet Number"); + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break; + } + + if(eos && track_module->simple_index.incomplete) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + else if(eos) return VC_CONTAINER_ERROR_EOS; + else return STREAM_STATUS(p_ctx); +} + +#if 0 +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_reader_index_find_packet( VC_CONTAINER_T *p_ctx, + unsigned int track, uint32_t *packet_num, bool forward ) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = 0; + uint32_t i, prev_packet_num = 0, next_packet_num; + bool eos = false; + + /* Sanity checking */ + if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_FAILED; + track_module = p_ctx->tracks[track]->priv->module; + if(!track_module->num_index_entries) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + if(!track_module->index_time_interval) return VC_CONTAINER_ERROR_CORRUPTED; + + status = SEEK(p_ctx, track_module->index_offset); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* Loop through all the entries in the index */ + for(i = 0; i < track_module->num_index_entries; i++) + { + next_packet_num = READ_U32(p_ctx, "Packet Number"); + SKIP_U16(p_ctx, "Packet Count"); + if(next_packet_num > *packet_num) break; + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break; + prev_packet_num = next_packet_num; + } + if(i == track_module->num_index_entries ) eos = true; + + if(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS && !eos) + { + if(forward) *packet_num = next_packet_num; + else *packet_num = prev_packet_num; + } + + if(eos && track_module->index_incomplete) return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + else if(eos) return VC_CONTAINER_ERROR_EOS; + else return STREAM_STATUS(p_ctx); +} +#endif + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_reader_find_next_frame( VC_CONTAINER_T *p_ctx, + unsigned int track, ASF_PACKET_STATE *p_state, bool keyframe, bool timeout ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t data_track, data_size; + unsigned int packets = 0; + + if(p_ctx->tracks[track]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) + keyframe = false; + + /* We still need to go to the right payload */ + while(status == VC_CONTAINER_SUCCESS && + (!timeout || packets++ < ASF_MAX_SEARCH_PACKETS)) + { + status = asf_read_next_payload_header( p_ctx, p_state, &data_track, &data_size ); + if(status != VC_CONTAINER_SUCCESS) break; + + if(data_track == track && ((p_state->stream_num >> 7) || !keyframe) && + !p_state->media_object_off) break; + + /* Skip payload */ + status = asf_read_next_payload(p_ctx, p_state, 0, &data_size ); + } + + return status; +} + +/*****************************************************************************/ +/* Helper for asf_reader_seek - seek when there is a top-level index (spec section 6.2) */ +static VC_CONTAINER_STATUS_T seek_by_top_level_index( + VC_CONTAINER_T *p_ctx, + int64_t *p_time, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned index; + uint64_t time = 0; + uint64_t block_address = module->top_level_index.blocks_offset; + uint64_t track_positions[ASF_TRACKS_MAX]; + + VC_CONTAINER_PARAM_UNUSED(mode); + LOG_DEBUG(p_ctx, "seek_by_top_level_index"); + + for (index = 0; index < ASF_TRACKS_MAX; ++index) + { + /* Set all to a stupid value */ + track_positions[index] = UINT64_MAX; + } + + /* Loop through the index blocks to find the one(s) that deal with the time(s) in question. + * Note that most ASF files only have one index block. */ + for (index = 0; index < module->top_level_index.block_count; ++index) + { + uint64_t block_duration, block_position; + uint32_t index_entry_count, stream; + LOG_DEBUG(p_ctx, "looking for index blocks at offset %"PRIu64, block_address); + status = SEEK(p_ctx, block_address); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* Read the number of entries for this index block. */ + index_entry_count = READ_U32(p_ctx, "Index Entry Count"); + + /* Turn into a duration */ + block_duration = (uint64_t)index_entry_count * module->top_level_index.entry_time_interval; + + /* Go through each stream */ + for (stream = 0; stream < p_ctx->tracks_num; ++stream) + { + /* Work out the track's target time */ + uint64_t track_time = *p_time + module->preroll + module->time_offset; + + /* Have we the correct index block for the seek time? */ + if ((time <= track_time) && (track_time < time + block_duration)) + { + /* We have the correct index block for the seek time. Work out where in it. */ + uint32_t block_index = (track_time - time) / module->top_level_index.entry_time_interval; + uint64_t active_specifier = module->top_level_index.active_specifiers[stream]; + uint64_t new_position; + + /* Read the Block Positions value for the correct specifier */ + status = SEEK(p_ctx, + block_address + INT64_C(4) + + active_specifier * INT64_C(8)); + if (status != VC_CONTAINER_SUCCESS) + { + return status; + } + block_position = READ_U32(p_ctx, "Block Position"); + + /* Read the target address for the stream */ + status = SEEK(p_ctx, block_address + 4 /* skip index entry count */ + + (UINT64_C(8) * module->top_level_index.specifiers_count) /* block positions */ + + (UINT64_C(4) * module->top_level_index.specifiers_count * block_index) /* prior index entries */ + + (UINT64_C(4) * active_specifier)); /* correct specifier */ + LOG_DEBUG(p_ctx, "reading at %"PRIu64, STREAM_POSITION(p_ctx)); + + new_position = module->data_offset + block_position + (uint64_t)READ_U32(p_ctx, "Offset"); + LOG_DEBUG(p_ctx, "actual address for stream %"PRIu32" = %"PRIu64, stream, new_position); + track_positions[stream] = new_position; + } + } + + /* Work out where the next block is */ + block_address += (UINT64_C(8) * module->top_level_index.specifiers_count) + + (UINT64_C(4) * module->top_level_index.specifiers_count * index_entry_count); + } + + return seek_to_positions(p_ctx, track_positions, p_time, flags, 0, 0); +} + +/* Helper for asf_reader_seek - + * Given a set of positions seek the tracks. The status is the result of physically seeking each one. + * It is expected that the positions will be before *p_time; if the flags require it search + * for the next keyframe that is at or above *p_time. */ +static VC_CONTAINER_STATUS_T seek_to_positions(VC_CONTAINER_T *p_ctx, uint64_t track_positions[ASF_TRACKS_MAX], + int64_t *p_time, VC_CONTAINER_SEEK_FLAGS_T flags, + unsigned int start_track, bool seek_on_start_track) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint64_t global_position = UINT64_MAX; + unsigned int lowest_track, index, tracks; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + int64_t track_best_pts[ASF_TRACKS_MAX]; + + if (*p_time == 0) + { + // Special case: Time 0 means beginning of file. Don't search for the matching packet(s). + memset(&module->packet_state, 0, sizeof(module->packet_state)); + module->packet_state.start = track_positions[0]; + status = SEEK(p_ctx, module->packet_state.start); + + // Set each track to using the global state + for(index = 0; index < p_ctx->tracks_num; index++) + { + p_ctx->tracks[index]->priv->module->p_packet_state = &module->packet_state; + } + + return status; + } + + for(tracks = 0, index = start_track; tracks < p_ctx->tracks_num; + tracks++, index = (index+1) % p_ctx->tracks_num) + { + uint32_t data_size; + + /* Use an on-stack packet state. We can't use the global state, as we must leave it at + * the lowest position. We can't use any track's private state, as we will move it past + * the desired location. */ + ASF_PACKET_STATE private_state; + memset(&private_state, 0, sizeof(private_state)); + + track_best_pts[index] = INT64_MAX; + + status = SEEK(p_ctx, track_positions[index]); + + /* loop until we find the packet we're looking for. + * stop when we've seen a big enough PTS, and are on a key frame */ + while(status == VC_CONTAINER_SUCCESS) + { + /* Get the next key-frame */ + status = asf_reader_find_next_frame(p_ctx, index, &private_state, true, true); + if(status == VC_CONTAINER_SUCCESS) + { + /* Get the PTS, if any */ + int64_t pts = (int64_t)private_state.media_object_pts; + + if(pts != ASF_UNKNOWN_PTS) + { + if ((track_best_pts[index] == INT64_MAX) /* we don't have a time yet */ + || (pts <= *p_time) /* it's before our target */ + || (flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) /* we want after target */ + { + /* Store this time. It's the best yet. */ + track_best_pts[index] = pts; + + /* Update the desired position */ + track_positions[index] = private_state.start + private_state.current_offset; + + /* Copy the local state into this track's private state */ + p_ctx->tracks[index]->priv->module->local_packet_state = private_state; + + LOG_DEBUG(p_ctx, "seek forward track %u to pts %"PRIu64, + index, track_best_pts[index]); + } + + /* If we've got to our target time we can stop. */ + if (pts >= *p_time) + { + /* Then stop. */ + break; + } + } + + status = asf_read_next_payload(p_ctx, &private_state, 0, &data_size ); + } + } + + /* If we are seeking using a specific track, usually this is the video track + * and we want all the other tracks to start at the same time or later */ + if (seek_on_start_track && start_track == index) + { + flags |= VC_CONTAINER_SEEK_FLAG_FORWARD; + *p_time = track_best_pts[index]; + } + + { + ASF_PACKET_STATE *p_state = &p_ctx->tracks[index]->priv->module->local_packet_state; + + LOG_DEBUG(p_ctx, "seek track %u to pts %"PRIu64" (key:%i,moo:%i)", + index, track_best_pts[index], p_state->stream_num >> 7, p_state->media_object_off); + } + } + + /* Find the smallest track address in track_positions. This will be the global position */ + /* Also the lowest PTS in track_best_pts, this will be the new global PTS */ + for (index = 0, lowest_track = 0; index < p_ctx->tracks_num; ++index) + { + /* If it is smaller, remember it */ + if (track_positions[index] < global_position) + { + global_position = track_positions[index]; + lowest_track = index; + } + + /* Put the lowest PTS into entry 0 of the array */ + if ((track_best_pts[index] != INT64_MAX) && (track_best_pts[index] < track_best_pts[0])) + { + track_best_pts[0] = track_best_pts[index]; + } + } + + /* Update the caller with the lowest real PTS, if any. (we may have already done this above) */ + if (track_best_pts[0] != INT64_MAX) + { + *p_time = track_best_pts[0]; + } + else + { + LOG_DEBUG(p_ctx, "no PTS suitable to update the caller"); + } + + /* As we did an extra read on the index track past the desired location seek back to it */ + status = SEEK(p_ctx, global_position); + + /* Copy the packet state for the stream with the lowest address into the global state */ + module->packet_state = p_ctx->tracks[lowest_track]->priv->module->local_packet_state; + + for(index = 0; index < p_ctx->tracks_num; index++) + { + VC_CONTAINER_TRACK_MODULE_T* track_mod = p_ctx->tracks[index]->priv->module; + + /* If the track position is the global position, or it is invalid, use the global state */ + if ((track_positions[index] <= global_position) || (track_positions[index] == UINT64_MAX)) + { + track_mod->p_packet_state = &module->packet_state; + } + else + { + /* Track is not at the global position. Use the local state. */ + LOG_DEBUG(p_ctx, "track %u local position %"PRIu64, index, track_positions[index]); + track_mod->p_packet_state = &track_mod->local_packet_state; + } + } + + return status; +} + +/*****************************************************************************/ +/* Seek to a location in the file, using whatever indices are available + * If flags bit VC_CONTAINER_SEEK_FLAG_FORWARD is set the position is guaranteed to + * be a keyframe at or after the requested location. Conversely if it is not set + * the position is guaranteed to be at or before the request. */ +static VC_CONTAINER_STATUS_T asf_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_time, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_EOS; /* initialised to known fail state */ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int stream; + + VC_CONTAINER_PARAM_UNUSED(mode); + + LOG_DEBUG(p_ctx, "asf_reader_seek"); + + /* Prefer the top-level index to the simple index - it has byte offsets not packet offsets, + * and is likely to have separate tables for every track */ + if (module->top_level_index.block_count) + { + status = seek_by_top_level_index(p_ctx, p_time, mode, flags); + } + else + { + uint64_t track_positions[ASF_TRACKS_MAX]; + int seek_track = -1; + uint32_t packet_num; + uint64_t new_position; + + if (*p_time == 0) + { + // Special optimisation - for time zero just go to the beginning. + packet_num = 0; + } + /* If there is a simple index use the packet number from it */ + else if (module->simple_index_track) + { + /* Correct time desired */ + uint64_t track_time = *p_time + module->preroll + module->time_offset; + + LOG_DEBUG(p_ctx, "using simple index"); + + /* Search the index for the correct packet */ + status = asf_reader_index_find_time(p_ctx, module->simple_index_track, track_time, + &packet_num, flags & VC_CONTAINER_SEEK_FLAG_FORWARD); + } + else + { + /* No index at all. Use arithmetic to guess the packet number. */ + LOG_DEBUG(p_ctx, "index not usable %u", (unsigned)status); + + if (module->packets_num == 0) + { + /* This is a broadcast stream, and we can't do the arithmetic. + * Set it to a value that will guarantee a seek fail. */ + LOG_DEBUG(p_ctx, "no packets in file"); + packet_num = UINT32_MAX; + } + else + { + packet_num = *p_time * module->packets_num / module->duration; + } + } + + /* calculate the byte address of the packet, relative to the start of data */ + new_position = (uint64_t)packet_num * (uint64_t)module->packet_size; + + LOG_DEBUG(p_ctx, "packet number %"PRIu32" approx byte offset %"PRIu64 , packet_num, new_position + module->data_offset); + if (new_position > (uint64_t)module->data_size) + { + new_position = module->data_size; + LOG_DEBUG(p_ctx, "arithmetic error, seeking to end of file %" PRIu64 , new_position + module->data_offset); + } + + new_position += module->data_offset; + + for(stream = 0; stream < p_ctx->tracks_num; stream++) + { + /* Use the 1st enabled video track as the seek stream */ + if(p_ctx->tracks[stream]->format->es_type == + VC_CONTAINER_ES_TYPE_VIDEO && + p_ctx->tracks[stream]->is_enabled && seek_track < 0) + seek_track = stream; + + track_positions[stream] = new_position; + } /* repeat for all tracks */ + + /* Work out if we actually got anywhere. If so, save the positions for the subsequent reads */ + status = seek_to_positions(p_ctx, track_positions, p_time, flags, + seek_track < 0 ? 0 : seek_track, seek_track >= 0); + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + +/* FIXME: metadata is currently shared across all readers so freeing + it is left to the common layer but this isn't necessarily + the best solution. + for(i = 0; i meta_num; i++) + free(p_ctx->meta[i]); + if(p_ctx->meta_num) free(p_ctx->meta); + p_ctx->meta_num = 0; +*/ + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + p_ctx->tracks_num = 0; + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + unsigned int i; + GUID_T guid; + + /* Check for an ASF top-level header object */ + if(PEEK_BYTES(p_ctx, (uint8_t *)&guid, sizeof(guid)) < sizeof(guid) || + memcmp(&guid, &asf_guid_header, sizeof(guid))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* + * We are dealing with an ASF file + */ + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + + /* Set the translation table to all error values */ + memset(&module->stream_number_to_index, 0xff, sizeof(module->stream_number_to_index)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + + /* Read the top level header object */ + status = asf_read_object(p_ctx, INT64_C(0)); + if(status != VC_CONTAINER_SUCCESS) + goto error; + + /* Bail out if we didn't find a track */ + if(!p_ctx->tracks_num) {status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE; goto error;} + + /* + * The top level data object must come next + */ + if(READ_GUID(p_ctx, &guid, "Object ID") != sizeof(guid) || + memcmp(&guid, &asf_guid_data, sizeof(guid))) + goto error; + + LOG_FORMAT(p_ctx, "Object Name: data"); + module->data_size = READ_U64(p_ctx, "Object Size"); + + /* If the data size was supplied remove the size of the common object header and the local header for this object */ + if(module->data_size) module->data_size -= ASF_OBJECT_HEADER_SIZE + 16 + 8 + 2; + + /* Sanity check the data object size */ + if(module->data_size < 0) + goto error; + + module->object_level++; + SKIP_GUID(p_ctx, "File ID"); + module->packets_num = READ_U64(p_ctx, "Total Data Packets"); + if(module->broadcast) module->packets_num = 0; + SKIP_U16(p_ctx, "Reserved"); + + if (module->packet_size) + { + LOG_DEBUG(p_ctx, "object size %"PRIu64" means %f packets", + module->data_size, (float)(module->data_size) / (float)(module->packet_size)); + } + + module->data_offset = STREAM_POSITION(p_ctx); + LOG_DEBUG(p_ctx, "expect end of data at address %"PRIu64, module->data_size + module->data_offset); + + module->object_level--; + + /* + * We now have all the information we really need to start playing the stream + */ + + /* Initialise state for all tracks */ + module->packet_state.start = module->data_offset; + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i]; + p_track->priv->module->p_packet_state = &module->packet_state; + } + + p_ctx->priv->pf_close = asf_reader_close; + p_ctx->priv->pf_read = asf_reader_read; + p_ctx->priv->pf_seek = asf_reader_seek; + + if(STREAM_SEEKABLE(p_ctx)) + { + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + p_ctx->capabilities |= VC_CONTAINER_CAPS_FORCE_TRACK; + } + + p_ctx->duration = module->duration; + + /* Check if we're done */ + if(!module->data_size || !STREAM_SEEKABLE(p_ctx)) + return VC_CONTAINER_SUCCESS; + + /* If the stream is seekable and not a broadcast stream, we want to use any index there + * might be at the end of the stream */ + + /* Seek back to the end of the data object */ + if( SEEK(p_ctx, module->data_offset + module->data_size) == VC_CONTAINER_SUCCESS) + { + /* This will catch the simple index object if it is there */ + do { + status = asf_read_object(p_ctx, INT64_C(0)); + } while(status == VC_CONTAINER_SUCCESS); + } + + for(i = 0; i < p_ctx->tracks_num; i++) + { + if(p_ctx->tracks[i]->priv->module->simple_index.offset) + LOG_DEBUG(p_ctx, "track %i has an index", i); + } + + /* Seek back to the start of the data */ + return SEEK(p_ctx, module->data_offset); + + error: + if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FORMAT_INVALID; + LOG_DEBUG(p_ctx, "asf: error opening stream (%i)", status); + if(module) asf_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open asf_reader_open +#endif diff --git a/containers/asf/asf_writer.c b/containers/asf/asf_writer.c new file mode 100755 index 0000000..60da7ce --- /dev/null +++ b/containers/asf/asf_writer.c @@ -0,0 +1,577 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +//#define ENABLE_CONTAINERS_LOG_FORMAT +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_writer_utils.h" +#include "containers/core/containers_logging.h" +#undef CONTAINER_HELPER_LOG_INDENT +#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->object_level + +VC_CONTAINER_STATUS_T asf_writer_open( VC_CONTAINER_T *p_ctx ); + +/****************************************************************************** +Defines. +******************************************************************************/ +#define ASF_TRACKS_MAX 16 +#define ASF_OBJECT_HEADER_SIZE (16+8) + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef enum { + ASF_OBJECT_TYPE_UNKNOWN = 0, + ASF_OBJECT_TYPE_HEADER, + ASF_OBJECT_TYPE_FILE_PROPS, + ASF_OBJECT_TYPE_STREAM_PROPS, + ASF_OBJECT_TYPE_EXT_STREAM_PROPS, + ASF_OBJECT_TYPE_DATA, + ASF_OBJECT_TYPE_SIMPLE_INDEX, + ASF_OBJECT_TYPE_INDEX, + ASF_OBJECT_TYPE_HEADER_EXT, + ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL, + ASF_OBJECT_TYPE_CODEC_LIST, + ASF_OBJECT_TYPE_CONTENT_DESCRIPTION, + ASF_OBJECT_TYPE_EXT_CONTENT_DESCRIPTION, + ASF_OBJECT_TYPE_STREAM_BITRATE_PROPS, + ASF_OBJECT_TYPE_LANGUAGE_LIST, + ASF_OBJECT_TYPE_METADATA, + ASF_OBJECT_TYPE_PADDING, +} ASF_OBJECT_TYPE_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + unsigned int stream_id; + uint64_t time_offset; + bool b_valid; + + uint64_t index_offset; + uint32_t num_index_entries; + int64_t index_time_interval; +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + int object_level; + uint32_t packet_size; + + VC_CONTAINER_TRACK_T *tracks[ASF_TRACKS_MAX]; + + VC_CONTAINER_WRITER_EXTRAIO_T null; + bool b_header_done; + + unsigned int current_track; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Static functions within this file. +******************************************************************************/ +static VC_CONTAINER_STATUS_T asf_write_object( VC_CONTAINER_T *p_ctx, ASF_OBJECT_TYPE_T object_type ); +static VC_CONTAINER_STATUS_T asf_write_object_header( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_header_ext( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_header_ext_internal( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_file_properties( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_stream_properties( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_ext_stream_properties( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_simple_index( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_index( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_data( VC_CONTAINER_T *p_ctx ); +#if 0 +static VC_CONTAINER_STATUS_T asf_write_object_codec_list( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_content_description( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T asf_write_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx ); +#endif + +static const GUID_T asf_guid_header = {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_file_props = {0x8CABDCA1, 0xA947, 0x11CF, {0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_stream_props = {0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_ext_stream_props = {0x14E6A5CB, 0xC672, 0x4332, {0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}}; +static const GUID_T asf_guid_data = {0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_simple_index = {0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}}; +static const GUID_T asf_guid_index = {0xD6E229D3, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}}; +static const GUID_T asf_guid_header_ext = {0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}}; +static const GUID_T asf_guid_codec_list = {0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}}; +static const GUID_T asf_guid_content_description = {0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; +static const GUID_T asf_guid_ext_content_description = {0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}}; +static const GUID_T asf_guid_stream_bitrate_props = {0x7BF875CE, 0x468D, 0x11D1, {0x8D, 0x82, 0x00, 0x60, 0x97, 0xC9, 0xA2, 0xB2}}; +static const GUID_T asf_guid_language_list = {0x7C4346A9, 0xEFE0, 0x4BFC, {0xB2, 0x29, 0x39, 0x3E, 0xDE, 0x41, 0x5C, 0x85}}; +static const GUID_T asf_guid_metadata = {0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}}; +static const GUID_T asf_guid_padding = {0x1806D474, 0xCADF, 0x4509, {0xA4, 0xBA, 0x9A, 0xAB, 0xCB, 0x96, 0xAA, 0xE8}}; + +static const GUID_T asf_guid_stream_type_video = {0xBC19EFC0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; +static const GUID_T asf_guid_stream_type_audio = {0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; +static const GUID_T asf_guid_error_correction = {0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}}; + +static struct { + const ASF_OBJECT_TYPE_T type; + const GUID_T *guid; + const char *psz_name; + VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T * ); + +} asf_object_list[] = +{ + {ASF_OBJECT_TYPE_HEADER, &asf_guid_header, "header", asf_write_object_header}, + {ASF_OBJECT_TYPE_FILE_PROPS, &asf_guid_file_props, "file properties", asf_write_object_file_properties}, + {ASF_OBJECT_TYPE_STREAM_PROPS, &asf_guid_stream_props, "stream properties", asf_write_object_stream_properties}, + {ASF_OBJECT_TYPE_EXT_STREAM_PROPS, &asf_guid_ext_stream_props, "extended stream properties", asf_write_object_ext_stream_properties}, + {ASF_OBJECT_TYPE_DATA, &asf_guid_data, "data", asf_write_object_data}, + {ASF_OBJECT_TYPE_SIMPLE_INDEX, &asf_guid_simple_index, "simple index", asf_write_object_simple_index}, + {ASF_OBJECT_TYPE_INDEX, &asf_guid_index, "index", asf_write_object_index}, + {ASF_OBJECT_TYPE_HEADER_EXT, &asf_guid_header_ext, "header extension", asf_write_object_header_ext}, + {ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL, &asf_guid_header_ext, "header extension", asf_write_object_header_ext_internal}, +#if 0 + {ASF_OBJECT_TYPE_CODEC_LIST, &asf_guid_codec_list, "codec list", asf_write_object_codec_list}, + {ASF_OBJECT_TYPE_CONTENT_DESCRIPTION, &asf_guid_content_description, "content description", asf_write_object_content_description}, + {ASF_OBJECT_TYPE_EXT_CONTENT_DESCRIPTION, &asf_guid_ext_content_description, "extended content description", 0}, + {ASF_OBJECT_TYPE_STREAM_BITRATE_PROPS, &asf_guid_stream_bitrate_props, "stream bitrate properties", asf_write_object_stream_bitrate_props}, +#endif + {ASF_OBJECT_TYPE_UNKNOWN, 0, "unknown", 0} +}; + +static GUID_T guid_null; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_write_object( VC_CONTAINER_T *p_ctx, ASF_OBJECT_TYPE_T type ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t object_size = 0; + unsigned int i; + + /* Find out which object we want to write */ + for( i = 0; asf_object_list[i].type && asf_object_list[i].type != type; i++ ); + + /* Check we found the requested type */ + if(!asf_object_list[i].type) + { + vc_container_assert(0); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* We need to find out the size of the object we're going to write. + * Because we want to be streamable, we can't just come back later to update the size field. + * The easiest way to find out the size of the data we're going to write is to write a dummy + * version of it and get the size from that. It is a bit wasteful but is so much easier and + * shouldn't really impact performance as there's no actual i/o involved. */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null)) + { + asf_object_list[i].pf_func(p_ctx); + object_size = STREAM_POSITION(p_ctx); + } + vc_container_writer_extraio_disable(p_ctx, &module->null); + + /* Special case for header extension internal function */ + if(type == ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL) + { + WRITE_U32(p_ctx, object_size, "Header Extension Data Size"); + /* Call the object specific writing function */ + status = asf_object_list[i].pf_func(p_ctx); + return status; + } + + /* Write the object header */ + + if(WRITE_GUID(p_ctx, asf_object_list[i].guid, "Object ID") != sizeof(GUID_T)) + return VC_CONTAINER_ERROR_EOS; + + LOG_FORMAT(p_ctx, "Object Name: %s", asf_object_list[i].psz_name); + + WRITE_U64(p_ctx, object_size + ASF_OBJECT_HEADER_SIZE, "Object Size"); + + module->object_level++; + + /* Call the object specific writing function */ + status = asf_object_list[i].pf_func(p_ctx); + + module->object_level--; + + if(status != VC_CONTAINER_SUCCESS) + LOG_DEBUG(p_ctx, "object %s appears to be corrupted", asf_object_list[i].psz_name); + + return status; +} + +static VC_CONTAINER_STATUS_T asf_write_object_header( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + WRITE_U32(p_ctx, 0, "Number of Header Objects"); /* FIXME: could use that */ + WRITE_U8(p_ctx, 0, "Reserved1"); + WRITE_U8(p_ctx, 0, "Reserved2"); + + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_FILE_PROPS); + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER_EXT); + + for(module->current_track = 0; module->current_track < p_ctx->tracks_num; + module->current_track++) + { + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_STREAM_PROPS); + } + + /* Codec List */ + /* Content Description */ + /* Stream Bitrate Properties */ + + return status; +} + +static VC_CONTAINER_STATUS_T asf_write_object_header_ext( VC_CONTAINER_T *p_ctx ) +{ + WRITE_GUID(p_ctx, &guid_null, "Reserved Field 1"); + WRITE_U16(p_ctx, 0, "Reserved Field 2"); + + return asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL); +} + +static VC_CONTAINER_STATUS_T asf_write_object_header_ext_internal( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + for(module->current_track = 0; module->current_track < p_ctx->tracks_num; + module->current_track++) + { + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_EXT_STREAM_PROPS); + } + + return status; +} + +static VC_CONTAINER_STATUS_T asf_write_object_file_properties( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + WRITE_GUID(p_ctx, &guid_null, "File ID"); + WRITE_U64(p_ctx, 0, "File Size"); + WRITE_U64(p_ctx, 0, "Creation Date"); + WRITE_U64(p_ctx, 0, "Data Packets Count"); + WRITE_U64(p_ctx, 0, "Play Duration"); + WRITE_U64(p_ctx, 0, "Send Duration"); + WRITE_U64(p_ctx, 0, "Preroll"); + WRITE_U32(p_ctx, 0, "Flags"); + WRITE_U32(p_ctx, module->packet_size, "Minimum Data Packet Size"); + WRITE_U32(p_ctx, module->packet_size, "Maximum Data Packet Size"); + WRITE_U32(p_ctx, 0, "Maximum Bitrate"); + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_bitmapinfoheader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *p_track ) +{ + uint32_t fourcc; + + /* Write the preamble to the BITMAPINFOHEADER */ + WRITE_U32(p_ctx, p_track->format->type->video.width, "Encoded Image Width"); + WRITE_U32(p_ctx, p_track->format->type->video.height, "Encoded Image Height"); + WRITE_U8(p_ctx, 0, "Reserved Flags"); + WRITE_U16(p_ctx, 40 + p_track->format->extradata_size, "Format Data Size"); + + /* Write BITMAPINFOHEADER structure */ + WRITE_U32(p_ctx, 40 + p_track->format->extradata_size, "Format Data Size"); + WRITE_U32(p_ctx, p_track->format->type->video.width, "Image Width"); + WRITE_U32(p_ctx, p_track->format->type->video.height, "Image Height"); + WRITE_U16(p_ctx, 0, "Reserved"); + WRITE_U16(p_ctx, 0, "Bits Per Pixel Count"); + fourcc = codec_to_fourcc(p_track->format->codec); + WRITE_BYTES(p_ctx, (char *)&fourcc, 4); /* Compression ID */ + LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc); + WRITE_U32(p_ctx, 0, "Image Size"); + WRITE_U32(p_ctx, 0, "Horizontal Pixels Per Meter"); + WRITE_U32(p_ctx, 0, "Vertical Pixels Per Meter"); + WRITE_U32(p_ctx, 0, "Colors Used Count"); + WRITE_U32(p_ctx, 0, "Important Colors Count"); + + WRITE_BYTES(p_ctx, p_track->format->extradata, p_track->format->extradata_size); + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_waveformatex( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *p_track) +{ + /* Write WAVEFORMATEX structure */ + WRITE_U16(p_ctx, codec_to_waveformat(p_track->format->codec), "Codec ID"); + WRITE_U16(p_ctx, p_track->format->type->audio.channels, "Number of Channels"); + WRITE_U32(p_ctx, p_track->format->type->audio.sample_rate, "Samples per Second"); + WRITE_U32(p_ctx, p_track->format->bitrate, "Average Number of Bytes Per Second"); + WRITE_U16(p_ctx, p_track->format->type->audio.block_align, "Block Alignment"); + WRITE_U16(p_ctx, p_track->format->type->audio.bits_per_sample, "Bits Per Sample"); + WRITE_U16(p_ctx, p_track->format->extradata_size, "Codec Specific Data Size"); + WRITE_BYTES(p_ctx, p_track->format->extradata, p_track->format->extradata_size); + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_object_stream_properties( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int track = module->current_track, ts_size = 0; + const GUID_T *p_guid = &guid_null; + + if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + p_guid = &asf_guid_stream_type_video; + ts_size = 11 + 40 + p_ctx->tracks[track]->format->extradata_size; + } + else if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { + p_guid = &asf_guid_stream_type_audio; + ts_size = 18 + p_ctx->tracks[track]->format->extradata_size; + } + + WRITE_GUID(p_ctx, p_guid, "Stream Type"); + WRITE_GUID(p_ctx, &asf_guid_error_correction, "Error Correction Type"); + WRITE_U64(p_ctx, 0, "Time Offset"); + WRITE_U32(p_ctx, ts_size, "Type-Specific Data Length"); + WRITE_U32(p_ctx, 0, "Error Correction Data Length"); + WRITE_U16(p_ctx, track + 1, "Flags"); + WRITE_U32(p_ctx, 0, "Reserved"); + + /* Type-Specific Data */ + if(ts_size) + { + if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + status = asf_write_bitmapinfoheader( p_ctx, p_ctx->tracks[track]); + else if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + status = asf_write_waveformatex( p_ctx, p_ctx->tracks[track]); + } + + /* Error Correction Data */ + + return status; +} + +static VC_CONTAINER_STATUS_T asf_write_object_ext_stream_properties( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + WRITE_U64(p_ctx, 0, "Start Time"); + WRITE_U64(p_ctx, 0, "End Time"); + WRITE_U32(p_ctx, 0, "Data Bitrate"); + WRITE_U32(p_ctx, 0, "Buffer Size"); + WRITE_U32(p_ctx, 0, "Initial Buffer Fullness"); + WRITE_U32(p_ctx, 0, "Alternate Data Bitrate"); + WRITE_U32(p_ctx, 0, "Alternate Buffer Size"); + WRITE_U32(p_ctx, 0, "Alternate Initial Buffer Fullness"); + WRITE_U32(p_ctx, 0, "Maximum Object Size"); + WRITE_U32(p_ctx, 0, "Flags"); + WRITE_U16(p_ctx, module->current_track + 1, "Stream Number"); + WRITE_U16(p_ctx, 0, "Stream Language ID Index"); + WRITE_U64(p_ctx, 0, "Average Time Per Frame"); + WRITE_U16(p_ctx, 0, "Stream Name Count"); + WRITE_U16(p_ctx, 0, "Payload Extension System Count"); + /* Stream Names */ + /* Payload Extension Systems */ + /* Stream Properties Object */ + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_object_index( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_object_simple_index( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T asf_write_object_data( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_write_header( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status; + + /* TODO Sanity check the tracks */ + + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER); + status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_DATA); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_writer_write( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PARAM_UNUSED(p_packet); + + if(!module->b_header_done) + { + module->b_header_done = true; + status = asf_write_header(p_ctx); + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_writer_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + + vc_container_writer_extraio_delete(p_ctx, &module->null); + free(module); + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format ) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_TRACK_T *track; + + /* TODO check we support this format */ + + if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* Allocate and initialise track data */ + if(p_ctx->tracks_num >= ASF_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + if(format->extradata_size) + { + status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); + if(status) goto error; + } + + vc_container_format_copy(track->format, format, format->extradata_size); + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + error: + vc_container_free_track(p_ctx, track); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T asf_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + + switch(operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + { + VC_CONTAINER_ES_FORMAT_T *p_format = + (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); + return asf_writer_add_track(p_ctx, p_format); + } + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + if(!module->b_header_done) + { + status = asf_write_header(p_ctx); + if(status != VC_CONTAINER_SUCCESS) return status; + module->b_header_done = true; + } + return VC_CONTAINER_SUCCESS; + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/****************************************************************************** +Global function definitions. +******************************************************************************/ + +VC_CONTAINER_STATUS_T asf_writer_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + unsigned int i; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "asf") && strcasecmp(extension, "wmv") && + strcasecmp(extension, "wma")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + + /* Create a null i/o writer to help us out in writing our data */ + status = vc_container_writer_extraio_create_null(p_ctx, &module->null); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* We'll only write the header once we've got all our tracks */ + + p_ctx->priv->pf_close = asf_writer_close; + p_ctx->priv->pf_write = asf_writer_write; + p_ctx->priv->pf_control = asf_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "asf: error opening stream"); + for(i = 0; i < ASF_TRACKS_MAX && p_ctx->tracks && p_ctx->tracks[i]; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + free(module); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open asf_writer_open +#endif diff --git a/containers/avi/CMakeLists.txt b/containers/avi/CMakeLists.txt new file mode 100755 index 0000000..f8589c1 --- /dev/null +++ b/containers/avi/CMakeLists.txt @@ -0,0 +1,19 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_avi ${LIBRARY_TYPE} avi_reader.c) + +target_link_libraries(reader_avi containers) + +install(TARGETS reader_avi DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_avi ${LIBRARY_TYPE} avi_writer.c) + +target_link_libraries(writer_avi containers) + +install(TARGETS writer_avi DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/avi/avi_reader.c b/containers/avi/avi_reader.c new file mode 100755 index 0000000..4c4c3f4 --- /dev/null +++ b/containers/avi/avi_reader.c @@ -0,0 +1,1521 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#define CONTAINER_IS_LITTLE_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_waveformat.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define AVISF_DISABLED 0x00000001 /*< If set stream should not be enabled by default. */ +#define AVIF_MUSTUSEINDEX 0x00000020 +#define AVIF_TRUSTCKTYPE 0x00000800 /*< (OpenDML) keyframe information reliable. */ + +#define AVIIF_LIST 0x00000001 +#define AVIIF_KEYFRAME 0x00000010 +#define AVIIF_NOTIME 0x00000100 + +#define AVI_INDEX_OF_INDEXES 0x00 +#define AVI_INDEX_OF_CHUNKS 0x01 +#define AVI_INDEX_2FIELD 0x01 +#define AVI_INDEX_DELTAFRAME 0x80000000 + +#define AVI_TRACKS_MAX 16 /*< We won't try to handle streams with more tracks than this */ + +#define AVI_TWOCC(a,b) ((a) | (b << 8)) + +#define AVI_SYNC_CHUNK(ctx) \ + while(STREAM_POSITION(ctx) & 1) \ + { \ + if (SKIP_BYTES(ctx, 1) != 1) break; \ + } + +#define AVI_SKIP_CHUNK(ctx, size) \ + do { \ + SKIP_BYTES(ctx, size); \ + AVI_SYNC_CHUNK(ctx); \ + } while(0) + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct AVI_TRACK_STREAM_STATE_T +{ + unsigned current_track_num; /**< Number of track currently being read */ + int64_t data_offset; /**< Offset within the stream to find the track data */ + uint32_t chunk_size; /**< Size of the current chunk being read */ + uint32_t chunk_data_left; /**< Data left from the current chunk being read */ + + unsigned extra_chunk_track_num; /**< Temporary storage for in-band data e.g. 'dd' + chunks */ + uint32_t extra_chunk_data[4]; + uint32_t extra_chunk_data_offs; + uint32_t extra_chunk_data_len; +} AVI_TRACK_STREAM_STATE_T; + +typedef struct AVI_TRACK_CHUNK_STATE_T +{ + uint64_t index; + uint64_t offs; /**< offset into bytestream consisting of all chunks for this track */ + int64_t time_pos; /**< pts of chunk (if known) */ + uint32_t flags; /**< flags associated with chunk */ + AVI_TRACK_STREAM_STATE_T local_state; + AVI_TRACK_STREAM_STATE_T *state; +} AVI_TRACK_CHUNK_STATE_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + int64_t time_start; /**< i.e. 'dwStart' in 'strh' (converted to microseconds) */ + int64_t duration; /**< i.e. 'dwLength' in 'strh' (converted to microseconds) */ + uint32_t time_num; /**< i.e. 'dwScale' in 'strh' */ + uint32_t time_den; /**< i.e. 'dwRate' in 'strh', time_num / time_den = + samples (or frames) / second for audio (or video) */ + uint32_t sample_size; /**< i.e. 'dwSampleSize' in 'strh' */ + + uint64_t index_offset; /**< Offset to the start of an OpenDML index i.e. 'indx' + (if available) */ + uint32_t index_size; /**< Size of the OpenDML index chunk */ + AVI_TRACK_CHUNK_STATE_T chunk; +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *tracks[AVI_TRACKS_MAX]; + uint64_t data_offset; /**< Offset to the start of data packets i.e. + the data in the 'movi' list */ + uint64_t data_size; /**< Size of the chunk containing data packets */ + uint64_t index_offset; /**< Offset to the start of index data e.g. + the data in a 'idx1' list */ + uint32_t index_size; /**< Size of the chunk containing index data */ + AVI_TRACK_STREAM_STATE_T state; +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +static VC_CONTAINER_STATUS_T avi_find_chunk(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T id, uint32_t *size) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size; + + do { + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_size = READ_U32(p_ctx, "Chunk size"); + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + if(chunk_id == id) + { + *size = chunk_size; + return VC_CONTAINER_SUCCESS; + } + /* Not interested in this chunk, skip it. */ + AVI_SKIP_CHUNK(p_ctx, chunk_size); + } while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS); + + return status; /* chunk not found */ +} + +static VC_CONTAINER_STATUS_T avi_find_list(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T fourcc, uint32_t *size) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size; + uint32_t peek_buf[1]; + + do { + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_size = READ_U32(p_ctx, "Chunk size"); + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + if(chunk_id == VC_FOURCC('L','I','S','T')) + { + if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if (peek_buf[0] == fourcc) + { + *size = chunk_size; + return VC_CONTAINER_SUCCESS; + } + } + /* Not interested in this chunk, skip it. */ + AVI_SKIP_CHUNK(p_ctx, chunk_size); + } while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS); + + return status; /* list not found */ +} + +static int64_t avi_stream_ticks_to_us(VC_CONTAINER_TRACK_MODULE_T *track_module, uint64_t ticks) +{ + int64_t time; + vc_container_assert(track_module->time_den != 0); + time = INT64_C(1000000) * track_module->time_num * ticks / track_module->time_den; + return time; +} + +static int64_t avi_calculate_chunk_time(VC_CONTAINER_TRACK_MODULE_T *track_module) +{ + if (track_module->sample_size == 0) + return track_module->time_start + avi_stream_ticks_to_us(track_module, track_module->chunk.index); + else + return track_module->time_start + avi_stream_ticks_to_us(track_module, + ((track_module->chunk.offs + (track_module->sample_size >> 1)) / track_module->sample_size)); +} + +static VC_CONTAINER_STATUS_T avi_read_stream_header_list(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_TRACK_MODULE_T *track_module) +{ + VC_CONTAINER_STATUS_T status; + int64_t list_offset; + uint32_t list_size, chunk_id, chunk_size; + + int stream_header_chunk_read = 0, stream_format_chunk_read = 0; + + /* Look for a 'strl' LIST (sub)chunk */ + status = avi_find_list(p_ctx, VC_FOURCC('s','t','r','l'), &chunk_size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "'strl' LIST not found for stream"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + list_offset = STREAM_POSITION(p_ctx); + list_size = chunk_size; + SKIP_FOURCC(p_ctx, "strl"); + + while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS && STREAM_POSITION(p_ctx) < list_offset + list_size) + { + int64_t offset = STREAM_POSITION(p_ctx); + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_size = READ_U32(p_ctx, "Chunk size"); + LOG_FORMAT(p_ctx, "chunk %4.4s, offset: %"PRIi64", size: %i", (char *)&chunk_id, offset, chunk_size); + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + if(chunk_id == VC_FOURCC('s','t','r','h')) + { + VC_CONTAINER_FOURCC_T fourcc_type, fourcc_handler; + uint32_t flags, scale, rate, div, start, length, sample_size; + + /* We won't accept more than one 'strh' per stream */ + if (stream_header_chunk_read) + { + LOG_DEBUG(p_ctx, "rejecting invalid 'strl', found more than one 'strh'"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + fourcc_type = READ_FOURCC(p_ctx, "fccType"); + fourcc_handler = READ_FOURCC(p_ctx, "fccHandler"); + flags = READ_U32(p_ctx, "dwFlags"); + SKIP_U16(p_ctx, "wPriority"); + SKIP_U16(p_ctx, "wLanguage"); + SKIP_U32(p_ctx, "dwInitialFrames"); + scale = READ_U32(p_ctx, "dwScale"); + rate = READ_U32(p_ctx, "dwRate"); + start = READ_U32(p_ctx, "dwStart"); + length = READ_U32(p_ctx, "dwLength"); + SKIP_U32(p_ctx, "dwSuggestedBufferSize"); + SKIP_U32(p_ctx, "dwQuality"); + sample_size = READ_U32(p_ctx, "dwSampleSize"); + SKIP_U16(p_ctx, "rcFrame.left"); + SKIP_U16(p_ctx, "rcFrame.top"); + SKIP_U16(p_ctx, "rcFrame.right"); + SKIP_U16(p_ctx, "rcFrame.bottom"); + + /* In AVI, sec/frame = scale/rate and frames/sec = rate/scale */ + if (rate == 0) + { + LOG_DEBUG(p_ctx, "invalid dwRate: 0, using 1 as a guess"); + LOG_DEBUG(p_ctx, "timestamps will almost certainly be wrong"); + rate = 1; + } + + div = vc_container_maths_gcd((int64_t)scale, (int64_t)rate); + scale = scale / div; + rate = rate / div; + + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + if(fourcc_type == VC_FOURCC('v','i','d','s')) + { + track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + track->format->type->video.frame_rate_num = rate; + track->format->type->video.frame_rate_den = scale; + + if (sample_size != 0) + { + LOG_DEBUG(p_ctx, "ignoring dwSampleSize (%d) for video stream", sample_size); + sample_size = 0; + } + } + else if(fourcc_type == VC_FOURCC('a','u','d','s')) + { + track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + /* VBR audio is going to be non-framed */ + track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + } + else if(fourcc_type == VC_FOURCC('t','x','t','s')) + track->format->es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; + + /* Don't overwrite any existing value (i.e. in the unlikely case where we + see 'strf' before 'strh') */ + if(!track->format->codec) track->format->codec = vfw_fourcc_to_codec(fourcc_handler); + + /* FIXME: enable this once read_media does the right thing */ + if (!(flags & AVISF_DISABLED) || 1) + track->is_enabled = 1; + + track_module->time_num = scale; + track_module->time_den = rate; + track_module->time_start = avi_stream_ticks_to_us(track_module, (uint64_t)start); + track_module->duration = avi_stream_ticks_to_us(track_module, (uint64_t)length); + track_module->sample_size = sample_size; + + p_ctx->duration = MAX(p_ctx->duration, track_module->duration); + + stream_header_chunk_read = 1; + } + else if(chunk_id == VC_FOURCC('s','t','r','f')) + { + uint8_t *buffer; + unsigned extra_offset = 0, extra_size = 0; + + /* We won't accept more than one 'strf' per stream */ + if (stream_format_chunk_read) + { + LOG_DEBUG(p_ctx, "rejecting invalid 'strl', found more than one 'strf'"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* Use the extradata buffer for reading in the entire 'strf' (should not be a large chunk) */ + if ((status = vc_container_track_allocate_extradata(p_ctx, track, chunk_size)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "failed to allocate memory for 'strf' (%d bytes)", chunk_size); + return status; + } + + buffer = track->priv->extradata; + if(READ_BYTES(p_ctx, buffer, chunk_size) != chunk_size) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + AVI_SYNC_CHUNK(p_ctx); + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + status = vc_container_bitmapinfoheader_to_es_format(buffer, chunk_size, &extra_offset, &extra_size, track->format); + } + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { + status = vc_container_waveformatex_to_es_format(buffer, chunk_size, &extra_offset, &extra_size, track->format); + if (track_module->sample_size != 0 && track_module->sample_size != track->format->type->audio.block_align) + { + LOG_DEBUG(p_ctx, "invalid dwSampleSize (%d), should match nBlockAlign (%d) for audio streams.", + track_module->sample_size, track->format->type->audio.block_align); + + /* Note that if nBlockAlign really is 0, strf is seriously broken... */ + if (track->format->type->audio.block_align != 0) + track_module->sample_size = track->format->type->audio.block_align; + } + else + { + /* Flawed muxers might only set nBlockAlign (i.e. not set dwSampleSize correctly). */ + if (track->format->type->audio.block_align == 1) + track_module->sample_size = 1; + } + } + + if (status != VC_CONTAINER_SUCCESS) return status; + + if (extra_size) + { + track->format->extradata = buffer + extra_offset; + track->format->extradata_size = extra_size; + } + + /* Codec specific fix-up */ + if (track->format->codec == VC_CONTAINER_CODEC_MP4A && + track->format->extradata_size) + { + /* This is going to be raw AAC so it will be framed */ + track_module->sample_size = 0; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + } + + /* WMA specific fix-up */ + if ((track->format->codec == VC_CONTAINER_CODEC_WMA1 || + track->format->codec == VC_CONTAINER_CODEC_WMA2 || + track->format->codec == VC_CONTAINER_CODEC_WMAP || + track->format->codec == VC_CONTAINER_CODEC_WMAL || + track->format->codec == VC_CONTAINER_CODEC_WMAV) && + track->format->extradata_size) + { + track_module->sample_size = 0; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + } + + stream_format_chunk_read = 1; + } + else if(chunk_id == VC_FOURCC('s','t','r','d')) + { + /* The data in a 'strd' chunk is either codec configuration data or DRM information, + we can safely assume it might be either as long as we don't overwrite any config + data read previously from a 'strf' chunk */ + if ((status = vc_container_track_allocate_drmdata(p_ctx, track, chunk_size)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "failed to allocate memory for 'strd' (%d bytes)", chunk_size); + return status; + } + + if(READ_BYTES(p_ctx, track->priv->drmdata, chunk_size) != chunk_size) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + AVI_SYNC_CHUNK(p_ctx); + + if (!track->format->extradata) + { + if (vc_container_track_allocate_extradata(p_ctx, track, chunk_size) == VC_CONTAINER_SUCCESS) + { + memcpy(track->format->extradata, track->priv->drmdata, chunk_size); + + track->format->extradata = track->priv->extradata; + track->format->extradata_size = chunk_size; + } + else + { + LOG_DEBUG(p_ctx, "failed to allocate memory for 'strd' (%d bytes)", chunk_size); + LOG_DEBUG(p_ctx, "no codec configuration data set"); + } + } + } + else if(chunk_id == VC_FOURCC('i','n','d','x')) + { + track_module->index_offset = STREAM_POSITION(p_ctx); + track_module->index_size = chunk_size; + } + else + { + /* Not interested in this chunk, skip it. */ + } + + /* Skip any left-over data */ + AVI_SKIP_CHUNK(p_ctx, offset + chunk_size + 8 - STREAM_POSITION(p_ctx) ); + } + + if (!stream_header_chunk_read || !stream_format_chunk_read) + { + LOG_DEBUG(p_ctx, "invalid 'strl', 'strh' and 'strf' are both required"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + return status; +} + +static VC_CONTAINER_STATUS_T avi_find_next_data_chunk(VC_CONTAINER_T *p_ctx, uint32_t *id, uint32_t *size) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size = 0; + uint32_t peek_buf[1]; + + do + { + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_size = READ_U32(p_ctx, "Chunk size"); + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + break; + + /* Check if this is a 'rec ' or a 'movi' LIST instead of a plain data chunk */ + if(chunk_id == VC_FOURCC('L','I','S','T')) + { + if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) + return VC_CONTAINER_ERROR_EOS; + if (peek_buf[0] == VC_FOURCC('r','e','c',' ')) + SKIP_FOURCC(p_ctx, "rec "); + else if (peek_buf[0] == VC_FOURCC('m','o','v','i')) + SKIP_FOURCC(p_ctx, "movi"); + else + AVI_SKIP_CHUNK(p_ctx, chunk_size); /* Not interested in this LIST chunk, skip it. */ + continue; + } + + /* Check if this is a 'AVIX' RIFF header instead of a data chunk */ + if(chunk_id == VC_FOURCC('R','I','F','F')) + { + if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) + return VC_CONTAINER_ERROR_EOS; + if (peek_buf[0] == VC_FOURCC('A','V','I','X')) + SKIP_FOURCC(p_ctx, "AVIX"); + else + AVI_SKIP_CHUNK(p_ctx, chunk_size); /* Not interested in this RIFF header, skip it. */ + continue; + } + + /* We treat only db/dc/dd or wb chunks as data */ + if((uint32_t)chunk_id >> 16 == AVI_TWOCC('d','c') || + (uint32_t)chunk_id >> 16 == AVI_TWOCC('d','b') || + (uint32_t)chunk_id >> 16 == AVI_TWOCC('d','d') || + (uint32_t)chunk_id >> 16 == AVI_TWOCC('w','b')) + { + *id = chunk_id; + *size = chunk_size; + break; + } + + /* Need to exit if a zero sized chunk encountered so we don't loop forever. */ + if(chunk_size == 0 && chunk_id == 0) return VC_CONTAINER_ERROR_EOS; + + /* Not interested in this chunk, skip it */ + AVI_SKIP_CHUNK(p_ctx, chunk_size); + } while ((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS); + + return status; +} + +static void avi_track_from_chunk_id(VC_CONTAINER_FOURCC_T chunk_id, uint16_t *data_type, uint16_t *track_num) +{ + *track_num = (((uint8_t*)&chunk_id)[0] - 48) * 10 + ((uint8_t*)&chunk_id)[1] - 48; + *data_type = (uint32_t)chunk_id >> 16; +} + +static VC_CONTAINER_STATUS_T avi_check_track(VC_CONTAINER_T *p_ctx, uint16_t data_type, uint16_t track_num) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if (track_num < p_ctx->tracks_num) + { + if (data_type == AVI_TWOCC('w','b') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_AUDIO) + { + LOG_DEBUG(p_ctx, "suspicious track type ('wb'), track %d is not an audio track", track_num); + status = VC_CONTAINER_ERROR_FAILED; + } + if (data_type == AVI_TWOCC('d','b') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) + { + LOG_DEBUG(p_ctx, "suspicious track type ('db'), track %d is not a video track", track_num); + status = VC_CONTAINER_ERROR_FAILED; + } + if (data_type == AVI_TWOCC('d','c') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) + { + LOG_DEBUG(p_ctx, "suspicious track type ('dc'), track %d is not a video track", track_num); + status = VC_CONTAINER_ERROR_FAILED; + } + if (data_type == AVI_TWOCC('d','d') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) + { + LOG_DEBUG(p_ctx, "suspicious track type ('dd'), track %d is not a video track", track_num); + status = VC_CONTAINER_ERROR_FAILED; + } + } + else + { + LOG_DEBUG(p_ctx, "invalid track number %d (no more than %d tracks expected)", + track_num, p_ctx->tracks_num); + status = VC_CONTAINER_ERROR_FAILED; + } + + return status; +} + +static int avi_compare_seek_time(int64_t chunk_time, int64_t seek_time, + int chunk_is_keyframe, VC_CONTAINER_SEEK_FLAGS_T seek_flags) +{ + if (chunk_time == seek_time && chunk_is_keyframe && !(seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + return 0; + + if (chunk_time > seek_time && chunk_is_keyframe && (seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + return 0; + + if (chunk_time > seek_time && !(seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + return 1; /* Chunk time is past seek time, caller should use the previous keyframe */ + + return -1; +} + +static VC_CONTAINER_STATUS_T avi_scan_legacy_index_chunk(VC_CONTAINER_T *p_ctx, int seek_track_num, + int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_TRACK_MODULE_T *track_module; + AVI_TRACK_CHUNK_STATE_T selected_chunk; + int64_t base_offset = module->data_offset; + int64_t selected_chunk_offset = base_offset + 4; + int32_t extra_offset = 0; + int first_chunk_offset = 1; + uint64_t position; + + SEEK(p_ctx, module->index_offset); + memset(&selected_chunk, 0, sizeof(selected_chunk)); + + while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS && + (uint64_t)STREAM_POSITION(p_ctx) < module->index_offset + module->index_size) + { + VC_CONTAINER_FOURCC_T chunk_id; + uint16_t data_type, track_num; + uint32_t chunk_flags, offset, size; + + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_flags = READ_U32(p_ctx, "dwFlags"); + offset = READ_U32(p_ctx, "dwOffset"); + size = READ_U32(p_ctx, "dwSize"); + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) break; + + /* Although it's rare, the offsets might be given from the start of the file + instead of the data chunk, we have to handle both cases. */ + if (first_chunk_offset) + { + if (offset > module->data_offset) base_offset = INT64_C(0); + selected_chunk_offset = base_offset + 4; + first_chunk_offset = 0; + } + + avi_track_from_chunk_id(chunk_id, &data_type, &track_num); + LOG_DEBUG(p_ctx, "reading track %"PRIu16, track_num); + + if (avi_check_track(p_ctx, data_type, track_num) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "skipping index entry for track %d/%d", track_num, p_ctx->tracks_num); + continue; + } + + track_module = p_ctx->tracks[track_num]->priv->module; + + if (data_type == AVI_TWOCC('d','d')) + { + if (track_num == seek_track_num) + track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_ENCRYPTED; + extra_offset = -(size + 8); + } + + /* If this entry does not affect timing, skip it */ + if ((chunk_flags & (AVIIF_LIST | AVIIF_NOTIME)) || data_type == AVI_TWOCC('d','d')) + continue; + + position = base_offset + offset + extra_offset; + extra_offset = INT64_C(0); + + /* Check validity of position */ + if (position <= module->data_offset /* || (*pos > module->data_offset + module->data_size*/) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + if (track_num == seek_track_num) + { + bool is_keyframe = true; + int res; + + if (p_ctx->tracks[track_num]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + is_keyframe = chunk_flags & AVIIF_KEYFRAME; + + if (is_keyframe) + track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + else + track_module->chunk.flags &= ~(VC_CONTAINER_PACKET_FLAG_KEYFRAME); + + res = avi_compare_seek_time(track_module->chunk.time_pos, *time, is_keyframe, flags); + if (res > 0) + break; /* We've found the keyframe we wanted */ + + if (is_keyframe) + { + selected_chunk_offset = position; + selected_chunk = track_module->chunk; + } + + if (res == 0) + break; /* We've found the keyframe we wanted */ + + track_module->chunk.index++; + track_module->chunk.offs += size; + track_module->chunk.time_pos = avi_calculate_chunk_time(track_module); + + LOG_DEBUG(p_ctx, "index %"PRIu64", offs %"PRIu64", time %"PRIi64"us", track_module->chunk.index, + track_module->chunk.offs, track_module->chunk.time_pos); + } + } + + if (status == VC_CONTAINER_SUCCESS || + /* When seeking backwards, always return the last good position */ + !(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + { + *pos = selected_chunk_offset; + track_module = p_ctx->tracks[seek_track_num]->priv->module; + track_module->chunk.index = selected_chunk.index; + track_module->chunk.offs = selected_chunk.offs; + track_module->chunk.flags = selected_chunk.flags; + track_module->chunk.time_pos = selected_chunk.time_pos; + *time = track_module->chunk.time_pos; + return VC_CONTAINER_SUCCESS; + } + + return VC_CONTAINER_ERROR_NOT_FOUND; +} + +static VC_CONTAINER_STATUS_T avi_scan_standard_index_chunk(VC_CONTAINER_T *p_ctx, uint64_t index_offset, + unsigned seek_track_num, int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; + VC_CONTAINER_TRACK_MODULE_T *track_module = NULL; + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size; + uint16_t data_type, track_num; + uint8_t index_type, index_sub_type; + uint32_t entry, entry_count = 0; + uint16_t entry_size; + uint64_t base_offset = UINT64_C(0); + uint64_t position = UINT64_C(0); + uint64_t prev_keyframe_offs = INT64_C(0); + AVI_TRACK_CHUNK_STATE_T prev_keyframe_chunk = { 0 }; + + SEEK(p_ctx, index_offset); + + chunk_id = READ_FOURCC(p_ctx, "Chunk ID"); + chunk_size = READ_U32(p_ctx, "Chunk Size"); + + entry_size = READ_U16(p_ctx, "wLongsPerEntry"); + index_sub_type = READ_U8(p_ctx, "bIndexSubType"); + index_type = READ_U8(p_ctx, "bIndexType"); + entry_count = READ_U32(p_ctx, "nEntriesInUse"); + chunk_id = READ_FOURCC(p_ctx, "dwChunkId"); + base_offset = READ_U64(p_ctx, "qwBaseOffset"); + SKIP_U32(p_ctx, "dwReserved"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + return status; + + avi_track_from_chunk_id(chunk_id, &data_type, &track_num); + status = avi_check_track(p_ctx, data_type, track_num); + if (status || chunk_size < 24 || track_num != seek_track_num) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + if (entry_size != 2 || index_sub_type != 0 || index_type != AVI_INDEX_OF_CHUNKS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + entry_count = MIN(entry_count, (chunk_size - 24) / (entry_size * 4)); + + track_module = p_ctx->tracks[seek_track_num]->priv->module; + + for (entry = 0; entry < entry_count; ++entry) + { + uint32_t chunk_offset; + int key_frame = 0; + + chunk_offset = READ_U32(p_ctx, "dwOffset"); + chunk_size = READ_U32(p_ctx, "dwSize"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + break; + + status = VC_CONTAINER_ERROR_NOT_FOUND; + + if (!(chunk_size & AVI_INDEX_DELTAFRAME)) + key_frame = 1; + chunk_size &= ~AVI_INDEX_DELTAFRAME; + + position = base_offset + chunk_offset - 8; + + if (key_frame) + track_module->chunk.flags = VC_CONTAINER_PACKET_FLAG_KEYFRAME; + else + track_module->chunk.flags = 0; + + if (time != NULL) + { + int res; + status = VC_CONTAINER_ERROR_NOT_FOUND; + res = avi_compare_seek_time(track_module->chunk.time_pos, *time, key_frame, flags); + + if (res == 0) + { + *pos = position; + *time = track_module->chunk.time_pos; + status = VC_CONTAINER_SUCCESS; + break; + } + else if (res > 0) + { + if (prev_keyframe_offs) + { + *pos = prev_keyframe_offs; + track_module->chunk = prev_keyframe_chunk; + *time = track_module->chunk.time_pos; + status = VC_CONTAINER_SUCCESS; + } + break; + } + + if (key_frame) + { + prev_keyframe_offs = position; + prev_keyframe_chunk = track_module->chunk; + } + } + else + { + /* Not seeking to a time position, but scanning + track chunk state up to a certain file position + instead */ + if (position >= *pos) + { + status = VC_CONTAINER_SUCCESS; + break; + } + } + + track_module->chunk.index++; + track_module->chunk.offs += chunk_size; + track_module->chunk.time_pos = avi_calculate_chunk_time(track_module); + } + + return status; +} + +static VC_CONTAINER_STATUS_T avi_scan_super_index_chunk(VC_CONTAINER_T *p_ctx, unsigned index_track_num, + int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; + VC_CONTAINER_FOURCC_T chunk_id; + uint64_t index_offset; + uint32_t index_size; + uint16_t data_type, track_num; + uint32_t entry, entry_count; + uint16_t entry_size; + uint8_t index_sub_type, index_type; + + index_offset = p_ctx->tracks[index_track_num]->priv->module->index_offset; + index_size = p_ctx->tracks[index_track_num]->priv->module->index_size; + + SEEK(p_ctx, index_offset); + + entry_size = READ_U16(p_ctx, "wLongsPerEntry"); + index_sub_type = READ_U8(p_ctx, "bIndexSubType"); + index_type = READ_U8(p_ctx, "bIndexType"); + entry_count = READ_U32(p_ctx, "nEntriesInUse"); + chunk_id = READ_FOURCC(p_ctx, "dwChunkId"); + SKIP_U32(p_ctx, "dwReserved0"); + SKIP_U32(p_ctx, "dwReserved1"); + SKIP_U32(p_ctx, "dwReserved2"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + return status; + + if (index_type == AVI_INDEX_OF_INDEXES) + { + avi_track_from_chunk_id(chunk_id, &data_type, &track_num); + status = avi_check_track(p_ctx, data_type, track_num); + if (status || index_size < 24 || track_num != index_track_num) return VC_CONTAINER_ERROR_FORMAT_INVALID; + + /* FIXME: We should probably support AVI_INDEX_2FIELD as well */ + if (entry_size != 4 || index_sub_type != 0) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + entry_count = MIN(entry_count, (index_size - 24) / entry_size); + + for (entry = 0; entry < entry_count; ++entry) + { + uint64_t entry_offset, standard_index_offset; + standard_index_offset = READ_U64(p_ctx, "qwOffset"); + SKIP_U32(p_ctx, "dwSize"); + SKIP_U32(p_ctx, "dwDuration"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + break; + + if (standard_index_offset == UINT64_C(0)) + { + status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Not plausible */ + break; + } + + entry_offset = STREAM_POSITION(p_ctx); + status = avi_scan_standard_index_chunk(p_ctx, standard_index_offset, index_track_num, time, flags, pos); + if (status != VC_CONTAINER_ERROR_NOT_FOUND) break; + SEEK(p_ctx, entry_offset); /* Move to next entry ('ix' chunk); */ + } + } + else if (index_type == AVI_INDEX_OF_CHUNKS) + { + /* It seems we are dealing with a standard index instead... */ + status = avi_scan_standard_index_chunk(p_ctx, index_offset, index_track_num, time, flags, pos); + } + else + { + status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + return status; +} + +static VC_CONTAINER_STATUS_T avi_read_dd_chunk( VC_CONTAINER_T *p_ctx, + AVI_TRACK_STREAM_STATE_T *p_state, uint16_t data_type, uint32_t chunk_size, + uint16_t track_num ) +{ + if (data_type == AVI_TWOCC('d','d')) + { + if (p_state->extra_chunk_data_len || + chunk_size > sizeof(p_state->extra_chunk_data)) + { + LOG_DEBUG(p_ctx, "cannot handle multiple consecutive 'dd' chunks"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + if(READ_BYTES(p_ctx, p_state->extra_chunk_data, chunk_size) != chunk_size) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + AVI_SYNC_CHUNK(p_ctx); + p_state->extra_chunk_track_num = track_num; + p_state->extra_chunk_data_len = chunk_size; + p_state->extra_chunk_data_offs = 0; + + return VC_CONTAINER_ERROR_CONTINUE; + } + else if (p_state->extra_chunk_data_len && + p_state->extra_chunk_track_num != track_num) + { + LOG_DEBUG(p_ctx, "dropping data from '%02ddd' chunk, not for this track (%d)", + p_state->extra_chunk_track_num, track_num); + p_state->extra_chunk_data_len = 0; + } + + return VC_CONTAINER_SUCCESS; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +static VC_CONTAINER_STATUS_T avi_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = NULL; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + AVI_TRACK_STREAM_STATE_T *p_state = &module->state; + + if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + { + p_state = p_ctx->tracks[p_packet->track]->priv->module->chunk.state; + } + + LOG_DEBUG(p_ctx, "seeking to %"PRIi64, p_state->data_offset); + SEEK(p_ctx, p_state->data_offset); + + if (p_state->chunk_data_left == 0) + { + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size; + uint16_t data_type, track_num; + + if ((status = avi_find_next_data_chunk(p_ctx, &chunk_id, &chunk_size)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "unable to find the next data chunk %d", status); + p_state->data_offset = STREAM_POSITION(p_ctx); + return status; + } + + avi_track_from_chunk_id(chunk_id, &data_type, &track_num); + + if (avi_check_track(p_ctx, data_type, track_num) != VC_CONTAINER_SUCCESS) + { + AVI_SKIP_CHUNK(p_ctx, chunk_size); + LOG_DEBUG(p_ctx, "skipping data for track %d/%d", track_num, p_ctx->tracks_num); + + p_state->data_offset = STREAM_POSITION(p_ctx); + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* If we are reading from the global state (i.e. normal read or forced + read from the track on the global state), and the track we found is + not on the global state, connect the two */ + if (p_state == &module->state && + p_ctx->tracks[track_num]->priv->module->chunk.state != &module->state) + { + int64_t next_chunk; + + /* The track's offset is past the current position, skip it as we are + not interested in track data from before the track's offset. If we + were to read it we would return the same data multiple times. */ + next_chunk = (STREAM_POSITION(p_ctx) + chunk_size + 1) & ~1; + if (p_ctx->tracks[track_num]->priv->module->chunk.state->data_offset > next_chunk) + { + AVI_SKIP_CHUNK(p_ctx, chunk_size); + LOG_DEBUG(p_ctx, "skipping track %d/%d as we have already read it", track_num, p_ctx->tracks_num); + p_state->data_offset = STREAM_POSITION(p_ctx); + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* The track state must be pointing to the current chunk. We need to + reconnect the track to the global state. */ + LOG_DEBUG(p_ctx, "reconnect track %u to the global state", track_num); + + p_ctx->tracks[track_num]->priv->module->chunk.state = &module->state; + + module->state = p_ctx->tracks[track_num]->priv->module->chunk.local_state; + + vc_container_assert(chunk_size >= p_state->chunk_data_left); + vc_container_assert(!p_state->chunk_data_left || + ((p_state->data_offset + p_state->chunk_data_left + 1) & ~1) == next_chunk); + vc_container_assert(p_state->current_track_num == track_num); + + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* If we are not forcing, or if we are and found the track we are + interested in, check for dd data and set the track module for the later code */ + if (!(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) || + (track_num == p_packet->track)) + { + if ((status = avi_read_dd_chunk(p_ctx, p_state, data_type, chunk_size, track_num)) != VC_CONTAINER_SUCCESS) + { + p_state->data_offset = STREAM_POSITION(p_ctx); + return status; + } + } + + p_state->chunk_size = p_state->chunk_data_left = chunk_size; + p_state->current_track_num = track_num; + } + + /* If there is data from another track skip past it */ + if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK && + p_state->current_track_num != p_packet->track) + { + p_state->data_offset = STREAM_POSITION(p_ctx); + + AVI_SKIP_CHUNK(p_ctx, p_state->chunk_data_left); + LOG_DEBUG(p_ctx, "skipping track %d/%d as we are ignoring it", + p_state->current_track_num, p_ctx->tracks_num); + + track_module = p_ctx->tracks[p_packet->track]->priv->module; + + /* Handle disconnection from global state */ + if (p_state == &module->state && + p_ctx->tracks[p_state->current_track_num]->priv->module->chunk.state == &module->state) + { + /* Make a copy of the global state */ + LOG_DEBUG(p_ctx, "using local state on track %d", p_packet->track); + track_module->chunk.local_state = module->state; + track_module->chunk.state = &track_module->chunk.local_state; + } + + track_module->chunk.state->data_offset = STREAM_POSITION(p_ctx); + track_module->chunk.state->chunk_data_left = 0; + + return VC_CONTAINER_ERROR_CONTINUE; + } + + track_module = p_ctx->tracks[p_state->current_track_num]->priv->module; + + if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + { + vc_container_assert(p_state->current_track_num == p_packet->track); + } + + LOG_DEBUG(p_ctx, "reading track %u chunk at time %"PRIi64"us, offset %"PRIu64, + p_state->current_track_num, track_module->chunk.time_pos, + track_module->chunk.offs); + if (p_state->extra_chunk_data_len) + track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_ENCRYPTED; + else + track_module->chunk.flags &= ~VC_CONTAINER_PACKET_FLAG_ENCRYPTED; + + if (p_packet) + { + p_packet->track = p_state->current_track_num; + p_packet->size = p_state->chunk_data_left + + p_state->extra_chunk_data_len; + p_packet->flags = track_module->chunk.flags; + + if (p_state->chunk_data_left == p_state->chunk_size) + { + p_packet->pts = track_module->chunk.time_pos; + if (track_module->sample_size == 0) + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME; + } + else + { + p_packet->pts = VC_CONTAINER_TIME_UNKNOWN; + if (track_module->sample_size == 0) + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + } + + p_packet->dts = VC_CONTAINER_TIME_UNKNOWN; + } + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + { + SKIP_BYTES(p_ctx, p_state->chunk_data_left); + AVI_SYNC_CHUNK(p_ctx); + p_state->chunk_data_left = 0; + p_state->extra_chunk_data_len = 0; + } + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + { + p_state->data_offset = STREAM_POSITION(p_ctx); + + LOG_DEBUG(p_ctx, "data position %"PRIi64, p_state->data_offset); + + return VC_CONTAINER_SUCCESS; + } + + if (p_packet) + { + uint8_t *data = p_packet->data; + uint32_t buffer_size = p_packet->buffer_size; + uint32_t size = 0; + uint32_t len; + + /* See if we need to insert extra data */ + if (p_state->extra_chunk_data_len) + { + len = MIN(buffer_size, p_state->extra_chunk_data_len); + memcpy(data, p_state->extra_chunk_data + p_state->extra_chunk_data_offs, len); + data += len; + buffer_size -= len; + size = len; + p_state->extra_chunk_data_len -= len; + p_state->extra_chunk_data_offs += len; + } + + /* Now try to read data into buffer */ + len = MIN(buffer_size, p_state->chunk_data_left); + READ_BYTES(p_ctx, data, len); + size += len; + p_state->chunk_data_left -= len; + p_packet->size = size; + + if (p_state->chunk_data_left) + p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + } + + if (p_state->chunk_data_left == 0) + { + AVI_SYNC_CHUNK(p_ctx); + track_module->chunk.index++; + track_module->chunk.offs += p_state->chunk_size; + track_module->chunk.flags = 0; + track_module->chunk.time_pos = avi_calculate_chunk_time(track_module); + } + + /* Update the track's position */ + p_state->data_offset = STREAM_POSITION(p_ctx); + + LOG_DEBUG(p_ctx, "data position %"PRIi64, p_state->data_offset); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint64_t position, pos; + AVI_TRACK_CHUNK_STATE_T chunk_state[AVI_TRACKS_MAX]; + AVI_TRACK_STREAM_STATE_T global_state; + unsigned seek_track_num, i; + + if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(p_ctx)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + LOG_DEBUG(p_ctx, "AVI seeking to %"PRIi64"us", *p_offset); + + /* Save current position and chunk state so we can restore it if we + hit an error whilst scanning index data */ + position = STREAM_POSITION(p_ctx); + for(i = 0; i < p_ctx->tracks_num; i++) + chunk_state[i] = p_ctx->tracks[i]->priv->module->chunk; + global_state = p_ctx->priv->module->state; + + /* Clear track state affected by a seek operation of any kind */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + p_ctx->tracks[i]->priv->module->chunk.index = INT64_C(0); + p_ctx->tracks[i]->priv->module->chunk.offs = INT64_C(0); + p_ctx->tracks[i]->priv->module->chunk.flags = 0; + p_ctx->tracks[i]->priv->module->chunk.time_pos = p_ctx->tracks[i]->priv->module->time_start; + p_ctx->tracks[i]->priv->module->chunk.state = &p_ctx->tracks[i]->priv->module->chunk.local_state; + p_ctx->tracks[i]->priv->module->chunk.local_state.chunk_data_left = UINT64_C(0); + p_ctx->tracks[i]->priv->module->chunk.local_state.chunk_size = UINT64_C(0); + p_ctx->tracks[i]->priv->module->chunk.local_state.extra_chunk_data_len = 0; + p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = module->data_offset + 4; + } + + /* Clear the global state */ + p_ctx->priv->module->state.chunk_data_left = UINT64_C(0); + p_ctx->priv->module->state.chunk_size = UINT64_C(0); + p_ctx->priv->module->state.extra_chunk_data_len = 0; + p_ctx->priv->module->state.data_offset = module->data_offset + 4; + + /* Choose track to use for seeking, favor video tracks and tracks + that are enabled */ + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->is_enabled && + p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break; + if(i == p_ctx->tracks_num) + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->is_enabled) break; + if(i == p_ctx->tracks_num) i = 0; + + LOG_DEBUG(p_ctx, "seek on track %d/%d", i, p_ctx->tracks_num); + seek_track_num = i; + + if (p_ctx->tracks[seek_track_num]->priv->module->index_offset) + { + LOG_DEBUG(p_ctx, "seeking using the super index"); + status = avi_scan_super_index_chunk(p_ctx, seek_track_num, p_offset, flags, &pos); + if (status != VC_CONTAINER_SUCCESS) goto error; + + /* As AVI chunks don't convey timestamp information, we need to scan all tracks + to the seek file position */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + if (p_ctx->tracks[i]->priv->module->index_offset && i != seek_track_num) + { + uint64_t track_pos; + int64_t track_time = *p_offset; + + status = avi_scan_super_index_chunk(p_ctx, i, &track_time, flags, &track_pos); + if (status != VC_CONTAINER_SUCCESS) goto error; + p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = track_pos; + } + } + } + else + { + LOG_DEBUG(p_ctx, "seeking using the legacy index"); + + /* The legacy index comes after data so it might not have been available at the + time the container was opened; if this is the case, see if we can find an index + now, if we can't, then there's no way we can proceed with the seek. */ + if(!module->index_offset) + { + uint32_t chunk_size; + + LOG_DEBUG(p_ctx, "no index offset, searching for one"); + + /* Locate data chunk and skip it */ + SEEK(p_ctx, module->data_offset); + AVI_SKIP_CHUNK(p_ctx, module->data_size); + /* Now search for the index */ + status = avi_find_chunk(p_ctx, VC_FOURCC('i','d','x','1'), &chunk_size); + if (status == VC_CONTAINER_SUCCESS) + { + /* Store offset to index data */ + module->index_offset = STREAM_POSITION(p_ctx); + module->index_size = chunk_size; + p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX; + p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG; + } + } + /* Check again, we may or may not have an index */ + if (!module->index_offset) + { + /* If there is no index and we are seeking to 0 we can assume the + correct location is the start of the data. Otherwise we are unable + to seek to a specified non-zero location without an index */ + if (*p_offset != INT64_C(0)) + { + LOG_DEBUG(p_ctx, "failed to find the legacy index, unable to seek"); + status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + goto error; + } + pos = module->data_offset; + } + else + { + LOG_DEBUG(p_ctx, "scanning the legacy index chunk"); + status = avi_scan_legacy_index_chunk(p_ctx, seek_track_num, p_offset, flags, &pos); + if (status != VC_CONTAINER_SUCCESS) goto error; + + for (i = 0; i < p_ctx->tracks_num; i++) + { + if (i != seek_track_num) + { + uint64_t track_pos = pos; + int64_t track_time = *p_offset; + + status = avi_scan_legacy_index_chunk(p_ctx, i, &track_time, flags, &track_pos); + if (status != VC_CONTAINER_SUCCESS) goto error; + p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = track_pos; + p_ctx->tracks[i]->priv->module->chunk.local_state.current_track_num = i; + } + } + } + } + + position = pos; + + /* Set the seek track's data offset */ + p_ctx->tracks[seek_track_num]->priv->module->chunk.local_state.data_offset = position; + p_ctx->tracks[seek_track_num]->priv->module->chunk.local_state.current_track_num = seek_track_num; + + /* Connect the earlier track(s) to the global state. Needs 2 passes */ + module->state.data_offset = INT64_MAX; + for(i = 0; i < p_ctx->tracks_num; i++) + { + if(p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset < + module->state.data_offset) + module->state = p_ctx->tracks[i]->priv->module->chunk.local_state; + } + for(i = 0; i < p_ctx->tracks_num; i++) + { + if(p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset == + module->state.data_offset) + p_ctx->tracks[i]->priv->module->chunk.state = &module->state; + } + + LOG_DEBUG(p_ctx, "seek to %"PRIi64", position %"PRIu64, *p_offset, pos); + + return SEEK(p_ctx, position); + +error: + p_ctx->priv->module->state = global_state; + for(i = 0; i < p_ctx->tracks_num; i++) + p_ctx->tracks[i]->priv->module->chunk = chunk_state[i]; + SEEK(p_ctx, position); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + free(module); + p_ctx->priv->module = 0; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + uint32_t chunk_size; + uint32_t peek_buf[3]; + unsigned int i; + uint32_t flags, num_streams; + int64_t offset; + + /* Check the RIFF chunk descriptor */ + if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 12) != 12) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if( peek_buf[0] != VC_FOURCC('R','I','F','F') ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if( peek_buf[2] != VC_FOURCC('A','V','I',' ') ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* + * We now know we are dealing with an AVI file + */ + SKIP_FOURCC(p_ctx, "RIFF ID"); + SKIP_U32(p_ctx, "fileSize"); + SKIP_FOURCC(p_ctx, "fileType"); + + /* Look for the 'hdrl' LIST (sub)chunk */ + status = avi_find_list(p_ctx, VC_FOURCC('h','d','r','l'), &chunk_size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "'hdrl' LIST not found"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + SKIP_FOURCC(p_ctx, "hdrl"); + + /* Now look for the 'avih' sub-chunk */ + status = avi_find_chunk(p_ctx, VC_FOURCC('a','v','i','h'), &chunk_size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "'avih' not found"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* Parse the 'avih' sub-chunk */ + SKIP_U32(p_ctx, "dwMicroSecPerFrame"); + SKIP_U32(p_ctx, "dwMaxBytesPerSec"); + SKIP_U32(p_ctx, "dwPaddingGranularity"); + flags = READ_U32(p_ctx, "dwFlags"); + SKIP_U32(p_ctx, "dwTotalFrames"); + SKIP_U32(p_ctx, "dwInitialFrames"); + num_streams = READ_U32(p_ctx, "dwStreams"); + SKIP_U32(p_ctx, "dwSuggestedBufferSize"); + SKIP_U32(p_ctx, "dwWidth"); + SKIP_U32(p_ctx, "dwHeight"); + SKIP_U32(p_ctx, "dwReserved0"); + SKIP_U32(p_ctx, "dwReserved1"); + SKIP_U32(p_ctx, "dwReserved2"); + SKIP_U32(p_ctx, "dwReserved3"); + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + goto error; + + /* Allocate our context and tracks */ + if ((module = malloc(sizeof(*module))) == NULL) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + + if (num_streams > AVI_TRACKS_MAX) + { + LOG_DEBUG(p_ctx, "cannot handle %u tracks, restricted to %d", num_streams, AVI_TRACKS_MAX); + num_streams = AVI_TRACKS_MAX; + } + + for (p_ctx->tracks_num = 0; p_ctx->tracks_num != num_streams; p_ctx->tracks_num++) + { + p_ctx->tracks[p_ctx->tracks_num] = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!p_ctx->tracks[p_ctx->tracks_num]) break; + } + if(p_ctx->tracks_num != num_streams) + { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + /* Try to read stream header list chunks ('strl') */ + for (i = 0; i != num_streams; ++i) + { + status = avi_read_stream_header_list(p_ctx, p_ctx->tracks[i], p_ctx->tracks[i]->priv->module); + if(status != VC_CONTAINER_SUCCESS) goto error; + } + + /* Look for the 'movi' LIST (sub)chunk */ + status = avi_find_list(p_ctx, VC_FOURCC('m','o','v','i'), &chunk_size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "'movi' LIST not found"); + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto error; + } + + /* Store offset to the start and size of data (the 'movi' LIST) */ + module->data_offset = STREAM_POSITION(p_ctx); + module->data_size = chunk_size; + + p_ctx->priv->pf_close = avi_reader_close; + p_ctx->priv->pf_read = avi_reader_read; + p_ctx->priv->pf_seek = avi_reader_seek; + + if (flags & AVIF_MUSTUSEINDEX) + { + LOG_DEBUG(p_ctx, "AVIF_MUSTUSEINDEX not supported, playback might not work properly"); + } + + /* If the stream is seekable, see if we can find an index (for at + least one of the tracks); even if we cannot find an index now, + one might become available later (e.g. when the stream grows + run-time), in that case we might want to report that we can seek + and re-search for the index again if or when we're requested to + seek. */ + if(STREAM_SEEKABLE(p_ctx)) + { + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + p_ctx->capabilities |= VC_CONTAINER_CAPS_FORCE_TRACK; + + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->priv->module->index_offset) break; + + if (i < p_ctx->tracks_num) + { + p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX; + if (flags & AVIF_TRUSTCKTYPE) + p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG; + } + else + { + /* Skip data first */ + AVI_SKIP_CHUNK(p_ctx, module->data_size); + /* Now search for the index */ + status = avi_find_chunk(p_ctx, VC_FOURCC('i','d','x','1'), &chunk_size); + if (status == VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "'idx1' found"); + /* Store offset to index data */ + module->index_offset = STREAM_POSITION(p_ctx); + module->index_size = chunk_size; + p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX; + p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG; + } + + /* Seek back to the start of the data */ + SEEK(p_ctx, module->data_offset); + } + } + + SKIP_FOURCC(p_ctx, "movi"); + + for (i = 0; i != num_streams; i++) + { + p_ctx->tracks[i]->priv->module->chunk.state = &p_ctx->priv->module->state; + } + p_ctx->priv->module->state.data_offset = STREAM_POSITION(p_ctx); + + /* Update the tracks to set their data offsets. This help with bad + interleaving, for example when there is all the video tracks followed + by all the audio tracks. It means we don't have to read through the + tracks we are not interested in when forcing a read from a given track, + as could be the case in the above example. If this fails we will fall + back to skipping track data. */ + offset = INT64_C(0); + avi_reader_seek(p_ctx, &offset, VC_CONTAINER_SEEK_MODE_TIME, VC_CONTAINER_SEEK_FLAG_PRECISE); + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; + return VC_CONTAINER_SUCCESS; + +error: + LOG_DEBUG(p_ctx, "error opening stream (%i)", status); + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + if (module) free(module); + p_ctx->priv->module = NULL; + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open avi_reader_open +#endif diff --git a/containers/avi/avi_writer.c b/containers/avi/avi_writer.c new file mode 100755 index 0000000..589ee13 --- /dev/null +++ b/containers/avi/avi_writer.c @@ -0,0 +1,1171 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include + +#define CONTAINER_IS_LITTLE_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_writer_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx ); + +/****************************************************************************** +Defines. +******************************************************************************/ +#define AVISF_DISABLED 0x00000001 /*< If set stream should not be enabled by default. */ +#define AVIF_HASINDEX 0x00000010 +#define AVIF_TRUSTCKTYPE 0x00000800 +#define AVIIF_KEYFRAME 0x00000010 + +#define AVI_INDEX_OF_INDEXES 0x00 +#define AVI_INDEX_OF_CHUNKS 0x01 +#define AVI_INDEX_DELTAFRAME 0x80000000 + +#define AVI_INDEX_ENTRY_SIZE 16 +#define AVI_SUPER_INDEX_ENTRY_SIZE 16 +#define AVI_STD_INDEX_ENTRY_SIZE 8 +#define AVI_FRAME_BUFFER_SIZE 100000 + +#define AVI_TRACKS_MAX 3 + +#define AVI_AUDIO_CHUNK_SIZE_LIMIT 16384 /*< Watermark limit for data chunks when 'dwSampleSize' + is non-zero */ + +#define AVI_END_CHUNK(ctx) \ + do { \ + if(STREAM_POSITION(ctx) & 1) WRITE_U8(ctx, 0, "AVI_END_CHUNK"); \ + } while(0) + +#define AVI_PACKET_KEYFRAME (VC_CONTAINER_PACKET_FLAG_KEYFRAME | VC_CONTAINER_PACKET_FLAG_FRAME_END) +#define AVI_PACKET_IS_KEYFRAME(flags) (((flags) & AVI_PACKET_KEYFRAME) == AVI_PACKET_KEYFRAME) + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + uint32_t chunk_index; /**< index of current chunk */ + uint32_t chunk_offs; /**< current offset into bytestream consisting of all + chunks for this track */ + uint32_t sample_size; /**< i.e. 'dwSampleSize' in 'strh' */ + uint32_t max_chunk_size; /**< largest chunk written so far */ + uint64_t index_offset; /**< Offset to the start of an OpenDML index for this track + i.e. 'indx' */ + uint32_t index_size; /**< Size of the OpenDML index for this track i.e. 'indx' */ +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *tracks[AVI_TRACKS_MAX]; + VC_CONTAINER_WRITER_EXTRAIO_T null_io; /**< Null I/O for calculating chunk sizes, etc. */ + VC_CONTAINER_WRITER_EXTRAIO_T temp_io; /**< I/O for temporary storage of index data */ + int headers_written; + + uint32_t header_list_offset; /**< Offset to the header list chunk ('hdrl') */ + uint32_t header_list_size; /**< Size of the header list chunk ('hdrl') */ + uint32_t data_offset; /**< Offset to the start of data packets i.e. + the data in the AVI RIFF 'movi' list */ + uint64_t data_size; /**< Size of the chunk containing data packets */ + uint32_t index_offset; /**< Offset to the start of index data e.g. + the data in an 'idx1' list */ + unsigned current_track_num; /**< Number of track currently being written */ + uint32_t chunk_size; /**< Final size of the current chunk being written (if known) */ + uint32_t chunk_data_written; /**< Data written to the current chunk so far */ + uint8_t *avi_frame_buffer; /**< For accumulating whole frames when seeking isn't available. */ + VC_CONTAINER_PACKET_T frame_packet; /**< Packet header for whole frame. */ + + VC_CONTAINER_STATUS_T index_status; +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Local Functions +******************************************************************************/ +static void avi_chunk_id_from_track_num( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_FOURCC_T *p_chunk_id, unsigned int track_num ) +{ + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[track_num]; + VC_CONTAINER_FOURCC_T chunk_id = 0; + char track_num_buf[3]; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + chunk_id = VC_FOURCC('0','0','d','c'); + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + chunk_id = VC_FOURCC('0','0','w','b'); + else + { + /* Note that avi_writer_add_track should ensure this + can't happen */ + *p_chunk_id = VC_FOURCC('J','U','N','K'); return; + } + + snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num); + memcpy(&chunk_id, track_num_buf, 2); + + *p_chunk_id = chunk_id; +} + +/*****************************************************************************/ +static void avi_index_chunk_id_from_track_num(VC_CONTAINER_FOURCC_T *p_chunk_id, + unsigned int track_num ) +{ + VC_CONTAINER_FOURCC_T chunk_id = 0; + char track_num_buf[3]; + + chunk_id = VC_FOURCC('i','x','0','0'); + + snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num); + memcpy(((uint8_t*)&chunk_id) + 2, track_num_buf, 2); + + *p_chunk_id = chunk_id; +} + +/*****************************************************************************/ +static uint32_t avi_num_chunks( VC_CONTAINER_T *p_ctx ) +{ + unsigned int i; + uint32_t num_chunks = 0; + for (i = 0; i < p_ctx->tracks_num; i++) + num_chunks += p_ctx->tracks[i]->priv->module->chunk_index; + + return num_chunks; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_finish_data_chunk( VC_CONTAINER_T *p_ctx, uint32_t chunk_size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if (chunk_size) + { + /* Rewrite the chunk size, this won't be efficient if it happens often */ + if (STREAM_SEEKABLE(p_ctx)) + { + SEEK(p_ctx, STREAM_POSITION(p_ctx) - chunk_size - 4); + WRITE_U32(p_ctx, chunk_size, "Chunk Size"); + SKIP_BYTES(p_ctx, chunk_size); + } + else + { + LOG_DEBUG(p_ctx, "warning, can't rewrite chunk size, data will be malformed"); + status = VC_CONTAINER_ERROR_FAILED; + } + } + + AVI_END_CHUNK(p_ctx); + + if (status != VC_CONTAINER_SUCCESS) status = STREAM_STATUS(p_ctx); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_index_entry( VC_CONTAINER_T *p_ctx, uint8_t track_num, + uint32_t chunk_size, int keyframe ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t deltaframe = keyframe ? 0 : AVI_INDEX_DELTAFRAME; + + vc_container_io_write_uint8(module->temp_io.io, track_num); + vc_container_io_write_be_uint32(module->temp_io.io, chunk_size | deltaframe); + + if (module->temp_io.io->status != VC_CONTAINER_SUCCESS) + { + module->index_status = module->temp_io.io->status; + LOG_DEBUG(p_ctx, "warning, couldn't store index data, index data will be incorrect"); + } + + return module->temp_io.io->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_read_index_entry( VC_CONTAINER_T *p_ctx, + unsigned int *p_track_num, uint32_t *p_chunk_size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t chunk_size; + uint8_t track_num; + + track_num = vc_container_io_read_uint8(module->temp_io.io); + chunk_size = vc_container_io_read_be_uint32(module->temp_io.io); + + /* This shouldn't really happen if the temporary I/O is reliable */ + if (track_num >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_FAILED; + + *p_track_num = track_num; + *p_chunk_size = chunk_size; + + return module->temp_io.io->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_stream_format_chunk(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, uint32_t chunk_size) +{ + VC_CONTAINER_STATUS_T status; + + WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','f'), "Chunk ID"); + WRITE_U32(p_ctx, chunk_size, "Chunk Size"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + status = vc_container_write_bitmapinfoheader(p_ctx, track->format); + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + status = vc_container_write_waveformatex(p_ctx, track->format); + + if (status != VC_CONTAINER_SUCCESS) return status; + + AVI_END_CHUNK(p_ctx); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_stream_header_chunk(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track) +{ + VC_CONTAINER_FOURCC_T fourcc_type = 0, fourcc_handler = 0; + uint32_t flags, scale = 0, rate = 0, div, start = 0, sample_size = 0; + uint16_t left = 0, right = 0, top = 0, bottom = 0; + uint32_t max_chunk_size, length = 0; + + WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','h'), "Chunk ID"); + WRITE_U32(p_ctx, 56, "Chunk Size"); + + if (!track->is_enabled) + flags = 0; /* AVISF_DISABLED; FIXME: write_media should set this correctly! */ + else + flags = 0; + + if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + fourcc_type = VC_FOURCC('v','i','d','s'); + sample_size = 0; + scale = track->format->type->video.frame_rate_den; + rate = track->format->type->video.frame_rate_num; + if (rate == 0 || scale == 0) + { + LOG_DEBUG(p_ctx, "invalid video framerate (%d/%d)", rate, scale); + LOG_DEBUG(p_ctx, "using 30/1 (playback timing will almost certainly be incorrect)"); + scale = 1; rate = 30; + } + + top = track->format->type->video.y_offset; + left = track->format->type->video.x_offset; + bottom = track->format->type->video.y_offset + track->format->type->video.visible_height; + right = track->format->type->video.x_offset + track->format->type->video.visible_width; + } + else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { + fourcc_type = VC_FOURCC('a','u','d','s'); + sample_size = track->format->type->audio.block_align; + scale = 1; + + if (track->format->type->audio.block_align) + rate = (track->format->bitrate / track->format->type->audio.block_align) >> 3; + + if (rate == 0) + { + rate = track->format->type->audio.sample_rate ? track->format->type->audio.sample_rate : 32000; + LOG_DEBUG(p_ctx, "invalid audio rate, using %d (playback timing will almost certainly be incorrect)", + rate); + } + } + else + { + /* avi_writer_add_track should ensure this can't happen */ + vc_container_assert(0); + } + + fourcc_handler = codec_to_vfw_fourcc(track->format->codec); + + div = vc_container_maths_gcd((int64_t)scale, (int64_t)rate); + scale /= div; + rate /= div; + + length = sample_size ? track->priv->module->chunk_offs : track->priv->module->chunk_index; + max_chunk_size = track->priv->module->max_chunk_size; + track->priv->module->sample_size = sample_size; + + WRITE_FOURCC(p_ctx, fourcc_type, "fccType"); + WRITE_FOURCC(p_ctx, fourcc_handler, "fccHandler"); + WRITE_U32(p_ctx, flags, "dwFlags"); + WRITE_U16(p_ctx, 0, "wPriority"); + WRITE_U16(p_ctx, 0, "wLanguage"); + WRITE_U32(p_ctx, 0, "dwInitialFrames"); + WRITE_U32(p_ctx, scale, "dwScale"); + WRITE_U32(p_ctx, rate, "dwRate"); + WRITE_U32(p_ctx, start, "dwStart"); + WRITE_U32(p_ctx, length, "dwLength"); + WRITE_U32(p_ctx, max_chunk_size, "dwSuggestedBufferSize"); + WRITE_U32(p_ctx, 0, "dwQuality"); + WRITE_U32(p_ctx, sample_size, "dwSampleSize"); + WRITE_U16(p_ctx, left, "rcFrame.left"); + WRITE_U16(p_ctx, top, "rcFrame.top"); + WRITE_U16(p_ctx, right, "rcFrame.right"); + WRITE_U16(p_ctx, bottom, "rcFrame.bottom"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_super_index_chunk(VC_CONTAINER_T *p_ctx, unsigned int index_track_num, + uint32_t index_size) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[index_track_num]->priv->module; + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t num_indices = 1; /* FIXME: support for multiple RIFF chunks (AVIX) */ + unsigned int i; + + if(module->null_io.refcount) + { + /* Assume that we're not actually writing the data, just want know the index chunk size */ + WRITE_BYTES(p_ctx, NULL, 8 + 24 + num_indices * (int64_t)AVI_SUPER_INDEX_ENTRY_SIZE); + return STREAM_STATUS(p_ctx); + } + + if (track_module->index_offset) + WRITE_FOURCC(p_ctx, VC_FOURCC('i','n','d','x'), "Chunk ID"); + else + WRITE_FOURCC(p_ctx, VC_FOURCC('J','U','N','K'), "Chunk ID"); + + WRITE_U32(p_ctx, index_size, "Chunk Size"); + + avi_chunk_id_from_track_num(p_ctx, &chunk_id, index_track_num); + WRITE_U16(p_ctx, 4, "wLongsPerEntry"); + WRITE_U8(p_ctx, 0, "bIndexSubType"); + WRITE_U8(p_ctx, AVI_INDEX_OF_INDEXES, "bIndexType"); + WRITE_U32(p_ctx, num_indices, "nEntriesInUse"); + WRITE_FOURCC(p_ctx, chunk_id, "dwChunkId"); + WRITE_U32(p_ctx, 0, "dwReserved0"); + WRITE_U32(p_ctx, 0, "dwReserved1"); + WRITE_U32(p_ctx, 0, "dwReserved2"); + + for (i = 0; i < num_indices; ++i) + { + uint64_t index_offset = track_module->index_offset; + uint32_t chunk_size = track_module->index_size; + uint32_t length = track_module->sample_size ? + track_module->chunk_offs : track_module->chunk_index; + WRITE_U64(p_ctx, index_offset, "qwOffset"); + WRITE_U32(p_ctx, chunk_size, "dwSize"); + WRITE_U32(p_ctx, length, "dwDuration"); + } + + AVI_END_CHUNK(p_ctx); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_stream_header_list(VC_CONTAINER_T *p_ctx, + unsigned int track_num, uint32_t list_size) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[track_num]; + VC_CONTAINER_STATUS_T status; + uint32_t chunk_size = 0; + + WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID"); + WRITE_U32(p_ctx, list_size, "LIST Size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','l'), "Chunk ID"); + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + /* Write the stream header chunk ('strh') */ + status = avi_write_stream_header_chunk(p_ctx, track); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Write the stream format chunk ('strf') */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_stream_format_chunk(p_ctx, track, 0); + chunk_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + status = avi_write_stream_format_chunk(p_ctx, track, chunk_size); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* If the track has DRM data, write it into the 'strd' chunk (we don't write + write codec configuration data into 'strd') */ + if (track->priv->drmdata && track->priv->drmdata_size) + { + WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','d'), "Chunk ID"); + WRITE_U32(p_ctx, track->priv->drmdata_size, "Chunk Size"); + WRITE_BYTES(p_ctx, track->priv->drmdata, track->priv->drmdata_size); + AVI_END_CHUNK(p_ctx); + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + } + + /* Write the super index chunk ('indx') */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_super_index_chunk(p_ctx, track_num, 0); + chunk_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + status = avi_write_super_index_chunk(p_ctx, track_num, chunk_size); + if (status != VC_CONTAINER_SUCCESS) return status; + + AVI_END_CHUNK(p_ctx); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_avi_header_chunk(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t bitrate = 0, width = 0, height = 0, frame_interval = 0; + uint32_t flags, num_chunks = 0, max_video_chunk_size = 0; + uint32_t num_streams = p_ctx->tracks_num; + unsigned int i; + + for (i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[i]->priv->module; + if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + width = track->format->type->video.width; + height = track->format->type->video.height; + if (track->format->type->video.frame_rate_num) + frame_interval = track->format->type->video.frame_rate_den * UINT64_C(1000000) / + track->format->type->video.frame_rate_num; + num_chunks = track_module->chunk_index; + max_video_chunk_size = track_module->max_chunk_size; + break; + } + } + + flags = (module->index_offset && module->index_status == VC_CONTAINER_SUCCESS) ? + (AVIF_HASINDEX | AVIF_TRUSTCKTYPE) : 0; + + WRITE_FOURCC(p_ctx, VC_FOURCC('a','v','i','h'), "Chunk ID"); + WRITE_U32(p_ctx, 56, "Chunk Size"); + WRITE_U32(p_ctx, frame_interval, "dwMicroSecPerFrame"); + WRITE_U32(p_ctx, bitrate >> 3, "dwMaxBytesPerSec"); + WRITE_U32(p_ctx, 0, "dwPaddingGranularity"); + WRITE_U32(p_ctx, flags, "dwFlags"); + WRITE_U32(p_ctx, num_chunks, "dwTotalFrames"); + WRITE_U32(p_ctx, 0, "dwInitialFrames"); + WRITE_U32(p_ctx, num_streams, "dwStreams"); + WRITE_U32(p_ctx, max_video_chunk_size, "dwSuggestedBufferSize"); + WRITE_U32(p_ctx, width, "dwWidth"); + WRITE_U32(p_ctx, height, "dwHeight"); + WRITE_U32(p_ctx, 0, "dwReserved0"); + WRITE_U32(p_ctx, 0, "dwReserved1"); + WRITE_U32(p_ctx, 0, "dwReserved2"); + WRITE_U32(p_ctx, 0, "dwReserved3"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_header_list( VC_CONTAINER_T *p_ctx, uint32_t header_list_size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + unsigned int i; + + WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID"); + WRITE_U32(p_ctx, header_list_size, "LIST Size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('h','d','r','l'), "Chunk ID"); + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + /* Write the main AVI header chunk ('avih') */ + if ((status = avi_write_avi_header_chunk(p_ctx)) != VC_CONTAINER_SUCCESS) + return status; + + for (i = 0; i < p_ctx->tracks_num; i++) + { + uint32_t list_size = 0; + + /* Write a stream header list chunk ('strl') */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_stream_header_list(p_ctx, i, 0); + if (status != VC_CONTAINER_SUCCESS) return status; + list_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + status = avi_write_stream_header_list(p_ctx, i, list_size); + if (status != VC_CONTAINER_SUCCESS) return status; + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_headers( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint32_t header_list_offset, header_list_size = 0; + + /* Write the header list chunk ('hdrl') */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_header_list(p_ctx, 0); + if (status != VC_CONTAINER_SUCCESS) return status; + header_list_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + header_list_offset = STREAM_POSITION(p_ctx); + status = avi_write_header_list(p_ctx, header_list_size); + if (status == VC_CONTAINER_SUCCESS && !module->header_list_offset) + { + module->header_list_offset = header_list_offset; + module->header_list_size = header_list_size; + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_legacy_index_chunk( VC_CONTAINER_T *p_ctx, uint32_t index_size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint32_t chunk_offset = 4; + unsigned int track_num; + + vc_container_assert(8 + avi_num_chunks(p_ctx) * INT64_C(16) <= (int64_t)ULONG_MAX); + + if(module->null_io.refcount) + { + /* Assume that we're not actually writing the data, + just want know the index size */ + WRITE_BYTES(p_ctx, NULL, 8 + avi_num_chunks(p_ctx) * (int64_t)AVI_INDEX_ENTRY_SIZE); + return STREAM_STATUS(p_ctx); + } + + module->index_offset = STREAM_POSITION(p_ctx); + + WRITE_FOURCC(p_ctx, VC_FOURCC('i','d','x','1'), "Chunk ID"); + WRITE_U32(p_ctx, index_size, "Chunk Size"); + + /* Scan through all written entries, convert to appropriate index format */ + vc_container_io_seek(module->temp_io.io, INT64_C(0)); + + while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS) + { + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size, flags; + + status = avi_read_index_entry(p_ctx, &track_num, &chunk_size); + if (status != VC_CONTAINER_SUCCESS) break; + + avi_chunk_id_from_track_num(p_ctx, &chunk_id, track_num); + flags = (chunk_size & AVI_INDEX_DELTAFRAME) ? 0 : AVIIF_KEYFRAME; + chunk_size &= ~AVI_INDEX_DELTAFRAME; + + WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID"); + WRITE_U32(p_ctx, flags, "dwFlags"); + WRITE_U32(p_ctx, chunk_offset, "dwOffset"); + WRITE_U32(p_ctx, chunk_size, "dwSize"); + + chunk_offset += ((chunk_size + 1) & ~1) + 8; + } + + AVI_END_CHUNK(p_ctx); + + /* Note that currently, we might write a partial index but still set AVIF_HASINDEX */ + /* if ( STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS ) module->index_offset = 0 */ + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_standard_index_chunk( VC_CONTAINER_T *p_ctx, unsigned int index_track_num, + uint32_t index_size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[index_track_num]->priv->module; + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_FOURCC_T chunk_id; + int64_t base_offset = module->data_offset + 12; + uint32_t num_chunks = track_module->chunk_index; + uint32_t chunk_offset = 4; + + vc_container_assert(32 + num_chunks * (int64_t)AVI_STD_INDEX_ENTRY_SIZE <= (int64_t)ULONG_MAX); + + if(module->null_io.refcount) + { + /* Assume that we're not actually writing the data, just want know the index chunk size */ + WRITE_BYTES(p_ctx, NULL, 8 + 24 + num_chunks * INT64_C(8)); + return STREAM_STATUS(p_ctx); + } + + track_module->index_offset = STREAM_POSITION(p_ctx); + track_module->index_size = index_size ? (index_size - 8) : 0; + + avi_index_chunk_id_from_track_num(&chunk_id, index_track_num); + WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID"); + WRITE_U32(p_ctx, index_size, "Chunk Size"); + + avi_chunk_id_from_track_num(p_ctx, &chunk_id, index_track_num); + WRITE_U16(p_ctx, 2, "wLongsPerEntry"); + WRITE_U8(p_ctx, 0, "bIndexSubType"); + WRITE_U8(p_ctx, AVI_INDEX_OF_CHUNKS, "bIndexType"); + WRITE_U32(p_ctx, num_chunks, "nEntriesInUse"); + WRITE_FOURCC(p_ctx, chunk_id, "dwChunkId"); + WRITE_U64(p_ctx, base_offset, "qwBaseOffset"); + WRITE_U32(p_ctx, 0, "dwReserved"); + + /* Scan through all written entries, convert to appropriate index format */ + vc_container_io_seek(module->temp_io.io, INT64_C(0)); + + while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS) + { + uint32_t chunk_size; + unsigned int track_num; + + status = avi_read_index_entry(p_ctx, &track_num, &chunk_size); + if (status != VC_CONTAINER_SUCCESS) break; + + if(track_num != index_track_num) continue; + + WRITE_U32(p_ctx, chunk_offset, "dwOffset"); + WRITE_U32(p_ctx, chunk_size, "dwSize"); + + chunk_offset += ((chunk_size + 1) & ~(1 | AVI_INDEX_DELTAFRAME)) + 12; + } + + AVI_END_CHUNK(p_ctx); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_legacy_index_data( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t chunk_size = 0; + + /* Write the legacy index chunk ('idx1') */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_legacy_index_chunk(p_ctx, 0); + if (status != VC_CONTAINER_SUCCESS) return status; + chunk_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + status = avi_write_legacy_index_chunk(p_ctx, chunk_size); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_write_standard_index_data( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t chunk_size = 0; + unsigned int i; + + /* Write the standard index chunks ('ix00') */ + for (i = 0; i < p_ctx->tracks_num; i++) + { + if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io)) + { + status = avi_write_standard_index_chunk(p_ctx, i, 0); + if (status != VC_CONTAINER_SUCCESS) return status; + chunk_size = STREAM_POSITION(p_ctx) - 8; + } + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + status = avi_write_standard_index_chunk(p_ctx, i, chunk_size); + if (status != VC_CONTAINER_SUCCESS) return status; + } + + return status; +} + +/*****************************************************************************/ +static int64_t avi_calculate_file_size( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t filesize = 0; + int refcount; + + /* Start from current file position */ + filesize = STREAM_POSITION(p_ctx); + + refcount = vc_container_writer_extraio_enable(p_ctx, &module->null_io); + vc_container_assert(refcount == 0); /* Although perfectly harmless, we should + not be called with the null i/o enabled. */ + VC_CONTAINER_PARAM_UNUSED(refcount); + + do { + /* If we know what the final size of the chunk is going to be, + we can use that here to avoid writing a partial final packet */ + WRITE_BYTES(p_ctx, NULL, p_packet->frame_size ? p_packet->frame_size : p_packet->size); + AVI_END_CHUNK(p_ctx); + + /* Index entries for the chunk */ + WRITE_BYTES(p_ctx, NULL, AVI_INDEX_ENTRY_SIZE + AVI_STD_INDEX_ENTRY_SIZE); + + /* Current standard index data */ + if (avi_write_standard_index_data(p_ctx) != VC_CONTAINER_SUCCESS) break; + + /* Current legacy index data */ + status = avi_write_legacy_index_data(p_ctx); + if (status != VC_CONTAINER_SUCCESS) break; + } while(0); + + filesize += STREAM_POSITION(p_ctx); + + vc_container_writer_extraio_disable(p_ctx, &module->null_io); + + return filesize; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +static VC_CONTAINER_STATUS_T avi_writer_write( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_T *track = NULL; + VC_CONTAINER_TRACK_MODULE_T *track_module = NULL; + + /* Check we have written headers before any data */ + if(!module->headers_written) + { + if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + module->headers_written = 1; + } + + /* Check that we have started the 'movi' list */ + if (!module->data_offset) + { + module->data_offset = STREAM_POSITION(p_ctx); + vc_container_assert(module->data_offset != INT64_C(0)); + + WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID"); + WRITE_U32(p_ctx, 0, "LIST Size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('m','o','v','i'), "Chunk ID"); + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + } + + /* If the container is passing in a frame from a new track but we + arent't finished with a chunk from another track we need to finish + that chunk first */ + if (module->chunk_data_written && p_packet->track != module->current_track_num) + { + track_module = p_ctx->tracks[module->current_track_num]->priv->module; + status = avi_finish_data_chunk(p_ctx, module->chunk_data_written); + avi_write_index_entry(p_ctx, module->current_track_num, module->chunk_data_written, 0); + track_module->chunk_index++; + track_module->chunk_offs += module->chunk_data_written; + track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written); + module->chunk_data_written = 0; + if (status != VC_CONTAINER_SUCCESS) return status; + } + + /* Check we are not about to go over the limit of total number of chunks */ + if (avi_num_chunks(p_ctx) == (uint32_t)ULONG_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + if(STREAM_SEEKABLE(p_ctx)) + { + /* Check we are not about to go over the maximum file size */ + if (avi_calculate_file_size(p_ctx, p_packet) >= (int64_t)ULONG_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + } + + /* FIXME: are we expected to handle this case or should it be picked up by the above layer? */ + vc_container_assert(!(module->chunk_data_written && (p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START))); + + track = p_ctx->tracks[p_packet->track]; + track_module = p_ctx->tracks[p_packet->track]->priv->module; + module->current_track_num = p_packet->track; + + if (module->chunk_data_written == 0) + { + /* This is the first fragment of the chunk */ + VC_CONTAINER_FOURCC_T chunk_id; + uint32_t chunk_size; + + avi_chunk_id_from_track_num(p_ctx, &chunk_id, p_packet->track); + + if (p_packet->frame_size) + { + /* We know what the final size of the chunk is going to be */ + chunk_size = module->chunk_size = p_packet->frame_size; + } + else + { + chunk_size = p_packet->size; + module->chunk_size = 0; + } + + WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID"); + if(STREAM_SEEKABLE(p_ctx) || p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) + { + /* If the output stream can seek we can fix up the frame size later, and if the + * packet holds the whole frame we won't need to, so write data straight out. */ + WRITE_U32(p_ctx, chunk_size, "Chunk Size"); + WRITE_BYTES(p_ctx, p_packet->data, p_packet->size); + } + else + { + vc_container_assert(module->avi_frame_buffer); + if(p_packet->size > AVI_FRAME_BUFFER_SIZE) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + module->frame_packet = *p_packet; + module->frame_packet.data = module->avi_frame_buffer; + memcpy(module->frame_packet.data, + p_packet->data, module->frame_packet.size); + } + + module->chunk_data_written = p_packet->size; + } + else + { + if(module->frame_packet.size > 0 && module->avi_frame_buffer) + { + if(module->frame_packet.size > 0) + { + if(module->frame_packet.size + p_packet->size > AVI_FRAME_BUFFER_SIZE) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + memcpy(module->frame_packet.data + module->frame_packet.size, + p_packet->data, p_packet->size); + module->frame_packet.size += p_packet->size; + } + } + else + { + WRITE_BYTES(p_ctx, p_packet->data, p_packet->size); + } + module->chunk_data_written += p_packet->size; + } + + if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) + return status; + + if ((p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) || + (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO && + track->format->type->audio.block_align && + module->chunk_data_written > AVI_AUDIO_CHUNK_SIZE_LIMIT)) + { + if(module->frame_packet.size > 0) + { + WRITE_U32(p_ctx, module->frame_packet.size, "Chunk Size"); + WRITE_BYTES(p_ctx, module->frame_packet.data, module->frame_packet.size); + p_packet->size = module->frame_packet.size; + module->frame_packet.size = 0; + } + + if (!module->chunk_size && module->chunk_data_written > p_packet->size) + { + /* The chunk size needs to be rewritten */ + status = avi_finish_data_chunk(p_ctx, module->chunk_data_written); + } + else + { + status = avi_finish_data_chunk(p_ctx, 0); + } + + if(!STREAM_SEEKABLE(p_ctx)) + { + /* If we are streaming then flush to avoid delaying data transport. */ + vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_FLUSH); + } + + if(STREAM_SEEKABLE(p_ctx)) + { + /* Keep track of data written so we can check we don't exceed file size and also for doing + * index fix-ups, but only do this if we are writing to a seekable IO. */ + avi_write_index_entry(p_ctx, p_packet->track, module->chunk_data_written, AVI_PACKET_IS_KEYFRAME(p_packet->flags)); + } + track_module->chunk_index++; + track_module->chunk_offs += module->chunk_data_written; + track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written); + module->chunk_data_written = 0; + + if (status != VC_CONTAINER_SUCCESS) return status; + } + + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_writer_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + /* If we arent't finished with a chunk we need to finish it first */ + if (module->chunk_data_written) + { + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track_num]->priv->module; + status = avi_finish_data_chunk(p_ctx, module->chunk_data_written); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "warning, writing failed, last chunk truncated"); + } + avi_write_index_entry(p_ctx, module->current_track_num, module->chunk_data_written, 0); + track_module->chunk_index++; + track_module->chunk_offs += module->chunk_data_written; + track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written); + module->chunk_data_written = 0; + } + + if(STREAM_SEEKABLE(p_ctx)) + { + uint32_t filesize; + + /* Write standard index data before finalising the size of the 'movi' list */ + status = avi_write_standard_index_data(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + { + module->index_status = status; + LOG_DEBUG(p_ctx, "warning, writing standard index data failed, file will be malformed"); + } + + /* FIXME: support for multiple RIFF chunks (AVIX) */ + module->data_size = STREAM_POSITION(p_ctx) - module->data_offset - 8; + + /* Now write the legacy index */ + status = avi_write_legacy_index_data(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + { + module->index_status = status; + LOG_DEBUG(p_ctx, "warning, writing legacy index data failed, file will be malformed"); + } + + /* If we can, do the necessary fixups for values not know at the + time of writing chunk headers */ + + /* Rewrite the AVI RIFF chunk size */ + filesize = (uint32_t)STREAM_POSITION(p_ctx); + SEEK(p_ctx, 4); + WRITE_U32(p_ctx, filesize, "fileSize"); + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "warning, rewriting 'fileSize' failed, file will be malformed"); + } + + /* Rewrite the header list chunk ('hdrl') */ + SEEK(p_ctx, module->header_list_offset); + status = avi_write_header_list(p_ctx, module->header_list_size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "warning, rewriting 'hdrl' failed, file will be malformed"); + } + + /* Rewrite the AVI RIFF 'movi' list size */ + SEEK(p_ctx, module->data_offset + 4); + WRITE_U32(p_ctx, module->data_size, "Chunk Size"); + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "warning, rewriting 'movi' list size failed, file will be malformed"); + } + } + + vc_container_writer_extraio_delete(p_ctx, &module->null_io); + if(module->temp_io.io) vc_container_writer_extraio_delete(p_ctx, &module->temp_io); + + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + p_ctx->tracks_num = 0; + p_ctx->tracks = NULL; + + if(module->avi_frame_buffer) free(module->avi_frame_buffer); + free(module); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_T *track = NULL; + + if (module->headers_written) return VC_CONTAINER_ERROR_FAILED; + + /* FIXME: should we check the format in more detail? */ + if((format->es_type != VC_CONTAINER_ES_TYPE_VIDEO && format->es_type != VC_CONTAINER_ES_TYPE_AUDIO) || + format->codec == VC_CONTAINER_CODEC_UNKNOWN) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* Allocate new track */ + if(p_ctx->tracks_num >= AVI_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + if(format->extradata_size) + { + status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); + if(status) goto error; + } + + status = vc_container_format_copy(track->format, format, format->extradata_size); + if(status) goto error; + + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + +error: + vc_container_free_track(p_ctx, track); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avi_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + + switch(operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + { + VC_CONTAINER_ES_FORMAT_T *format = + (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); + return avi_writer_add_track(p_ctx, format); + } + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + { + if(!module->headers_written) + { + if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + module->headers_written = 1; + return VC_CONTAINER_SUCCESS; + } + else + return VC_CONTAINER_ERROR_FAILED; + } + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/****************************************************************************** +Global function definitions. +******************************************************************************/ +VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx ) +{ + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = 0; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "avi") && strcasecmp(extension, "divx")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + + /* Create a null i/o writer to help us out in writing our data */ + status = vc_container_writer_extraio_create_null(p_ctx, &module->null_io); + if(status != VC_CONTAINER_SUCCESS) goto error; + + if(STREAM_SEEKABLE(p_ctx)) + { + /* Create a temporary i/o writer for storage of index data while we are writing */ + status = vc_container_writer_extraio_create_temp(p_ctx, &module->temp_io); + if(status != VC_CONTAINER_SUCCESS) goto error; + } + else + { + module->avi_frame_buffer = malloc(AVI_FRAME_BUFFER_SIZE); + if(!module->avi_frame_buffer) + { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + } + module->frame_packet.size = 0; + + p_ctx->tracks = module->tracks; + + /* Write the RIFF chunk descriptor */ + WRITE_FOURCC(p_ctx, VC_FOURCC('R','I','F','F'), "RIFF ID"); + WRITE_U32(p_ctx, 0, "fileSize"); + WRITE_FOURCC(p_ctx, VC_FOURCC('A','V','I',' '), "fileType"); + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; + + p_ctx->priv->pf_close = avi_writer_close; + p_ctx->priv->pf_write = avi_writer_write; + p_ctx->priv->pf_control = avi_writer_control; + + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "error opening stream"); + p_ctx->tracks_num = 0; + p_ctx->tracks = NULL; + if(module) + { + if(module->avi_frame_buffer) free(module->avi_frame_buffer); + free(module); + } + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open avi_writer_open +#endif diff --git a/containers/binary/CMakeLists.txt b/containers/binary/CMakeLists.txt new file mode 100755 index 0000000..c159b40 --- /dev/null +++ b/containers/binary/CMakeLists.txt @@ -0,0 +1,19 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_binary ${LIBRARY_TYPE} binary_reader.c) + +target_link_libraries(reader_binary containers) + +install(TARGETS reader_binary DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_binary ${LIBRARY_TYPE} binary_writer.c) + +target_link_libraries(writer_binary containers) + +install(TARGETS writer_binary DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/binary/binary_reader.c b/containers/binary/binary_reader.c new file mode 100755 index 0000000..0bcf389 --- /dev/null +++ b/containers/binary/binary_reader.c @@ -0,0 +1,267 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define DEFAULT_BLOCK_SIZE (1024*16) +/* Work-around for JPEG because our decoder expects that much at the start */ +#define DEFAULT_JPEG_BLOCK_SIZE (1024*80) + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + unsigned int default_block_size; + unsigned int block_size; + bool init; + + VC_CONTAINER_STATUS_T status; + +} VC_CONTAINER_MODULE_T; + +static struct +{ + const char *ext; + VC_CONTAINER_ES_TYPE_T type; + VC_CONTAINER_FOURCC_T codec; + +} extension_to_format_table[] = +{ + /* Audio */ + {"mp3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MPGA}, + {"aac", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A}, + {"adts", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A}, + {"ac3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AC3}, + {"ec3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_EAC3}, + {"amr", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AMRNB}, + {"awb", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AMRWB}, + {"evrc", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_EVRC}, + {"dts", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_DTS}, + + /* Video */ + {"m1v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP1V}, + {"m2v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP2V}, + {"m4v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V}, + {"mp4v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V}, + {"h263", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263}, + {"263", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263}, + {"h264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264}, + {"264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264}, + {"mvc", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MVC}, + {"vc1l", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_WVC1}, + + /* Image */ + {"gif", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_GIF}, + {"jpg", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_JPEG}, + {"jpeg", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_JPEG}, + {"png", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_PNG}, + {"ppm", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_PPM}, + {"tga", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_TGA}, + {"bmp", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_BMP}, + + {"bin", 0, 0}, + {0, 0, 0} +}; + +static struct +{ + const char *ext; + VC_CONTAINER_ES_TYPE_T type; + VC_CONTAINER_FOURCC_T codec; + +} bin_extension_to_format_table[] = +{ + {"m4v.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V}, + {"263.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263}, + {"264.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264}, + {0, 0, 0} +}; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int size; + + if(module->status != VC_CONTAINER_SUCCESS) + return module->status; + + if(!module->block_size) + { + module->block_size = module->default_block_size; + module->init = 0; + } + + packet->size = module->block_size; + packet->dts = packet->pts = VC_CONTAINER_TIME_UNKNOWN; + if(module->init) packet->dts = packet->pts = 0; + packet->track = 0; + packet->flags = 0; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + size = SKIP_BYTES(p_ctx, module->block_size); + module->block_size -= size; + module->status = STREAM_STATUS(p_ctx); + return module->status; + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + size = MIN(module->block_size, packet->buffer_size); + size = READ_BYTES(p_ctx, packet->data, size); + module->block_size -= size; + packet->size = size; + + module->status = size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx); + return module->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(module); + VC_CONTAINER_PARAM_UNUSED(offset); + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T *p_ctx ) +{ + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + VC_CONTAINER_ES_TYPE_T es_type = 0; + VC_CONTAINER_FOURCC_T codec = 0; + unsigned int i; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check if the extension is supported */ + if(!extension || !vc_uri_path(p_ctx->priv->uri)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + for(i = 0; extension_to_format_table[i].ext; i++) + { + if(strcasecmp(extension, extension_to_format_table[i].ext)) + continue; + + es_type = extension_to_format_table[i].type; + codec = extension_to_format_table[i].codec; + break; + } + if(!extension_to_format_table[i].ext) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* If this a .bin file we look in our bin list */ + for(i = 0; !codec && bin_extension_to_format_table[i].ext; i++) + { + if(!strstr(vc_uri_path(p_ctx->priv->uri), bin_extension_to_format_table[i].ext) && + !strstr(extension, bin_extension_to_format_table[i].ext)) + continue; + + es_type = bin_extension_to_format_table[i].type; + codec = bin_extension_to_format_table[i].codec; + break; + } + if(!codec) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks_num = 1; + p_ctx->tracks = &module->track; + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + p_ctx->tracks[0]->format->es_type = es_type; + p_ctx->tracks[0]->format->codec = codec; + p_ctx->tracks[0]->is_enabled = true; + module->default_block_size = DEFAULT_BLOCK_SIZE; + if(codec == VC_CONTAINER_CODEC_JPEG) + module->default_block_size = DEFAULT_JPEG_BLOCK_SIZE; + module->block_size = module->default_block_size; + module->init = 1; + + /* + * We now have all the information we really need to start playing the stream + */ + + p_ctx->priv->pf_close = binary_reader_close; + p_ctx->priv->pf_read = binary_reader_read; + p_ctx->priv->pf_seek = binary_reader_seek; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "binary: error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open binary_reader_open +#endif diff --git a/containers/binary/binary_writer.c b/containers/binary/binary_writer.c new file mode 100755 index 0000000..b4580ea --- /dev/null +++ b/containers/binary/binary_writer.c @@ -0,0 +1,160 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Supported extensions +******************************************************************************/ +static const char *extensions[] = +{ "mp3", "aac", "adts", "ac3", "ec3", "amr", "awb", "evrc", "dts", + "m1v", "m2v", "mp4v", "h263", "263", "h264", "264", "mvc", + "bin", 0 +}; + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T * ); + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_writer_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_writer_write( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + WRITE_BYTES(p_ctx, packet->data, packet->size); + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T binary_writer_control( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_ES_FORMAT_T *format; + VC_CONTAINER_TRACK_T *track; + VC_CONTAINER_STATUS_T status; + + switch(operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + format = (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); + + /* Allocate and initialise track data */ + if(p_ctx->tracks_num >= 1) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, 0); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + if(format->extradata_size) + { + status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); + if(status != VC_CONTAINER_SUCCESS) + { + vc_container_free_track(p_ctx, track); + return status; + } + WRITE_BYTES(p_ctx, format->extradata, format->extradata_size); + } + + vc_container_format_copy(track->format, format, format->extradata_size); + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + return VC_CONTAINER_SUCCESS; + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T *p_ctx ) +{ + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + unsigned int i; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + for(i = 0; extensions[i]; i++) + if(!strcasecmp(extension, extensions[i])) break; + if(!extensions[i]) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = &module->track; + + p_ctx->priv->pf_close = binary_writer_close; + p_ctx->priv->pf_write = binary_writer_write; + p_ctx->priv->pf_control = binary_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "binary: error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open binary_writer_open +#endif diff --git a/containers/containers.h b/containers/containers.h new file mode 100755 index 0000000..4b090c9 --- /dev/null +++ b/containers/containers.h @@ -0,0 +1,746 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_H +#define VC_CONTAINERS_H + +/** \file containers.h + * Public API for container readers and writers + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "containers/containers_types.h" + +/** \defgroup VcContainerApi Container API + * API for container readers and writers */ +/* @{ */ + +/** Status codes returned by the container API */ +typedef enum +{ + VC_CONTAINER_SUCCESS = 0, /**< No error */ + VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED, /**< Format of container is not supported */ + VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED, /**< Format of container uses unsupported features */ + VC_CONTAINER_ERROR_FORMAT_INVALID, /**< Format of container is invalid */ + VC_CONTAINER_ERROR_CORRUPTED, /**< Container is corrupted */ + VC_CONTAINER_ERROR_URI_NOT_FOUND, /**< URI could not be found */ + VC_CONTAINER_ERROR_URI_OPEN_FAILED, /**< URI could not be opened */ + VC_CONTAINER_ERROR_OUT_OF_MEMORY, /**< Out of memory */ + VC_CONTAINER_ERROR_OUT_OF_SPACE, /**< Out of disk space (used when writing) */ + VC_CONTAINER_ERROR_OUT_OF_RESOURCES, /**< Out of resources (other than memory) */ + VC_CONTAINER_ERROR_EOS, /**< End of stream reached */ + VC_CONTAINER_ERROR_LIMIT_REACHED, /**< User defined limit reached (used when writing) */ + VC_CONTAINER_ERROR_BUFFER_TOO_SMALL, /**< Given buffer is too small for data to be copied */ + VC_CONTAINER_ERROR_INCOMPLETE_DATA, /**< Requested data is incomplete */ + VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE, /**< Container doesn't have any track */ + VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED, /**< Format of the track is not supported */ + VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION, /**< The requested operation is not supported */ + VC_CONTAINER_ERROR_INVALID_ARGUMENT, /**< The argument provided is invalid */ + VC_CONTAINER_ERROR_CONTINUE, /**< The requested operation was interrupted and needs to be tried again */ + VC_CONTAINER_ERROR_ABORTED, /**< The requested operation was aborted */ + VC_CONTAINER_ERROR_NOT_FOUND, /**< The requested data was not found */ + VC_CONTAINER_ERROR_DRM_NOT_AUTHORIZED, /**< The DRM was not authorized */ + VC_CONTAINER_ERROR_DRM_EXPIRED, /**< The DRM has expired */ + VC_CONTAINER_ERROR_DRM_FAILED, /**< Generic DRM error */ + VC_CONTAINER_ERROR_FAILED, /**< Generic error */ + VC_CONTAINER_ERROR_NOT_READY /**< The container was not yet able to carry out the operation. */ +} VC_CONTAINER_STATUS_T; + +/** Four Character Code type used to identify codecs, etc. */ +typedef uint32_t VC_CONTAINER_FOURCC_T; + +/** Type definition for language codes. + * Language are defined as ISO639 Alpha-3 codes (http://en.wikipedia.org/wiki/List_of_ISO_639-2_codes) */ +typedef uint8_t VC_CONTAINER_LANGUAGE_T[3]; + +/** Enumeration of the character encodings supported. */ +typedef enum { + VC_CONTAINER_CHAR_ENCODING_UNKNOWN = 0, /**< Encoding is unknown */ + VC_CONTAINER_CHAR_ENCODING_UTF8 /**< UTF8 encoding */ +} VC_CONTAINER_CHAR_ENCODING_T; + +/** \name Container Capabilities + * The following flags are exported by containers to describe their capabilities */ +/* @{ */ +/** Type definition for container capabilities */ +typedef uint32_t VC_CONTAINER_CAPABILITIES_T; +/** The container can seek */ +#define VC_CONTAINER_CAPS_CAN_SEEK 0x1 +/** Seeking is fast. The absence of this flag can be used as a hint to avoid seeking as much as possible */ +#define VC_CONTAINER_CAPS_SEEK_IS_FAST 0x2 +/** The container controls the pace at which it is reading the data */ +#define VC_CONTAINER_CAPS_CAN_CONTROL_PACE 0x4 +/** The container provides an index. This basically means that seeking will be precise and fast */ +#define VC_CONTAINER_CAPS_HAS_INDEX 0x8 +/** The container provides keyframe information */ +#define VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG 0x10 +/** The container supports adding tracks after TRACK_ADD_DONE control message has been sent */ +#define VC_CONTAINER_CAPS_DYNAMIC_TRACK_ADD 0x20 +/** The container supports forcing reading of a given track */ +#define VC_CONTAINER_CAPS_FORCE_TRACK 0x40 +/* @} */ + +/** \defgroup VcContainerMetadata Container Metadata + * Container metadata contains descriptive information which is associated with the multimedia data */ +/* @{ */ + +/** Enumeration of the different metadata keys available. */ +typedef enum { + /* Metadata of global scope */ + VC_CONTAINER_METADATA_KEY_TITLE = VC_FOURCC('t','i','t','l'), + VC_CONTAINER_METADATA_KEY_ARTIST = VC_FOURCC('a','r','t','i'), + VC_CONTAINER_METADATA_KEY_ALBUM = VC_FOURCC('a','l','b','m'), + VC_CONTAINER_METADATA_KEY_DESCRIPTION = VC_FOURCC('d','e','s','c'), + VC_CONTAINER_METADATA_KEY_YEAR = VC_FOURCC('y','e','a','r'), + VC_CONTAINER_METADATA_KEY_GENRE = VC_FOURCC('g','e','n','r'), + VC_CONTAINER_METADATA_KEY_TRACK = VC_FOURCC('t','r','a','k'), + VC_CONTAINER_METADATA_KEY_LYRICS = VC_FOURCC('l','y','r','x'), + + VC_CONTAINER_METADATA_KEY_UNKNOWN = 0 + +} VC_CONTAINER_METADATA_KEY_T; + +/** Definition of the metadata type. + * This type is used to store one element of metadata */ +typedef struct VC_CONTAINER_METADATA_T +{ + /** Identifier for the type of metadata the value refers to. + * Using an enum for the id will mean that a list of possible values will have to be + * defined and maintained. This might limit extensibility and customisation.\n + * Maybe it would be better to use a FOURCC or even a string here. */ + VC_CONTAINER_METADATA_KEY_T key; + + VC_CONTAINER_LANGUAGE_T language; /**< Language code for the metadata */ + VC_CONTAINER_CHAR_ENCODING_T encoding; /**< Encoding of the metadata */ + + /** Metadata value. This value is defined as null-terminated UTF-8 string.\n + * Do we want to support other types than strings (e.g. integer) ?\n + * We need an encoding conversion library! */ + char *value; + + /** Size of the memory area reserved for metadata value (including any + * terminating characters). */ + unsigned int size; +} VC_CONTAINER_METADATA_T; +/* @} */ + +/** \defgroup VcContainerESFormat Container Elementary Stream Format + * This describes the format of an elementary stream associated with a track */ +/* @{ */ + +/** Enumeration of the different types of elementary streams. + * This divides elementary streams into 4 big categories. */ +typedef enum +{ + VC_CONTAINER_ES_TYPE_UNKNOWN, /**< Unknown elementary stream type */ + VC_CONTAINER_ES_TYPE_AUDIO, /**< Audio elementary stream */ + VC_CONTAINER_ES_TYPE_VIDEO, /**< Video elementary stream */ + VC_CONTAINER_ES_TYPE_SUBPICTURE /**< Sub-picture elementary stream (e.g. subtitles, overlays) */ + +} VC_CONTAINER_ES_TYPE_T; + +/** Definition of a video format. + * This describes the properties specific to a video stream */ +typedef struct VC_CONTAINER_VIDEO_FORMAT_T +{ + uint32_t width; /**< Width of the frame */ + uint32_t height; /**< Height of the frame */ + uint32_t visible_width; /**< Width of the visible area of the frame */ + uint32_t visible_height; /**< Height of the visible area of the frame */ + uint32_t x_offset; /**< Offset to the start of the visible width */ + uint32_t y_offset; /**< Offset to the start of the visible height */ + uint32_t frame_rate_num; /**< Frame rate numerator */ + uint32_t frame_rate_den; /**< Frame rate denominator */ + uint32_t par_num; /**< Pixel aspect ratio numerator */ + uint32_t par_den; /**< Pixel aspect ratio denominator */ +} VC_CONTAINER_VIDEO_FORMAT_T; + +/** Enumeration for the different channel locations */ +typedef enum +{ + VC_CONTAINER_AUDIO_CHANNEL_LEFT = 0, /**< Left channel */ + VC_CONTAINER_AUDIO_CHANNEL_RIGHT, /**< Right channel */ + VC_CONTAINER_AUDIO_CHANNEL_CENTER, /**< Center channel */ + VC_CONTAINER_AUDIO_CHANNEL_LOW_FREQUENCY, /**< Low frequency channel */ + VC_CONTAINER_AUDIO_CHANNEL_BACK_LEFT, /**< Back left channel */ + VC_CONTAINER_AUDIO_CHANNEL_BACK_RIGHT, /**< Back right channel */ + VC_CONTAINER_AUDIO_CHANNEL_BACK_CENTER, /**< Back center channel */ + VC_CONTAINER_AUDIO_CHANNEL_SIDE_LEFT, /**< Side left channel */ + VC_CONTAINER_AUDIO_CHANNEL_SIDE_RIGHT, /**< Side right channel */ + + VC_CONTAINER_AUDIO_CHANNELS_MAX = 32 /**< Maximum number of channels supported */ + +} VC_CONTAINER_AUDIO_CHANNEL_T; + +/** \name Audio format flags + * \anchor audioformatflags + * The following flags describe properties of an audio stream */ +/* @{ */ +#define VC_CONTAINER_AUDIO_FORMAT_FLAG_CHANNEL_MAPPING 0x1 /**< Channel mapping available */ +/* @} */ + +/** Definition of an audio format. + * This describes the properties specific to an audio stream */ +typedef struct VC_CONTAINER_AUDIO_FORMAT_T +{ + uint32_t channels; /**< Number of audio channels */ + uint32_t sample_rate; /**< Sample rate */ + + uint32_t bits_per_sample; /**< Bits per sample */ + uint32_t block_align; /**< Size of a block of data */ + + uint32_t flags; /**< Flags describing the audio format. + * See \ref audioformatflags "Audio format flags". */ + + /** Mapping of the channels in order of appearance */ + VC_CONTAINER_AUDIO_CHANNEL_T channel_mapping[VC_CONTAINER_AUDIO_CHANNELS_MAX]; + + uint16_t gap_delay; /**< Delay introduced by the encoder. Used for gapless playback */ + uint16_t gap_padding; /**< Padding introduced by the encoder. Used for gapless playback */ + + /** Replay gain information. First element is the track information and the second + * is the album information. */ + struct { + float peak; /**< Peak value (full range is 1.0) */ + float gain; /**< Gain value in dB */ + } replay_gain[2]; + +} VC_CONTAINER_AUDIO_FORMAT_T; + +/** Definition of a subpicture format. + * This describes the properties specific to a subpicture stream */ +typedef struct VC_CONTAINER_SUBPICTURE_FORMAT_T +{ + VC_CONTAINER_CHAR_ENCODING_T encoding; /**< Encoding for text based subpicture formats */ + uint32_t x_offset; /**< Width offset to the start of the subpicture */ + uint32_t y_offset; /**< Height offset to the start of the subpicture */ +} VC_CONTAINER_SUBPICTURE_FORMAT_T; + +/** \name Elementary stream format flags + * \anchor esformatflags + * The following flags describe properties of an elementary stream */ +/* @{ */ +#define VC_CONTAINER_ES_FORMAT_FLAG_FRAMED 0x1 /**< Elementary stream is framed */ +/* @} */ + +/** Definition of the type specific format. + * This describes the type specific information of the elementary stream. */ +typedef union +{ + VC_CONTAINER_AUDIO_FORMAT_T audio; /**< Audio specific information */ + VC_CONTAINER_VIDEO_FORMAT_T video; /**< Video specific information */ + VC_CONTAINER_SUBPICTURE_FORMAT_T subpicture; /**< Subpicture specific information */ +} VC_CONTAINER_ES_SPECIFIC_FORMAT_T; + +/** Definition of an elementary stream format */ +typedef struct VC_CONTAINER_ES_FORMAT_T +{ + VC_CONTAINER_ES_TYPE_T es_type; /**< Type of the elementary stream */ + VC_CONTAINER_FOURCC_T codec; /**< Coding of the elementary stream */ + VC_CONTAINER_FOURCC_T codec_variant; /**< If set, indicates a variant of the coding */ + + VC_CONTAINER_ES_SPECIFIC_FORMAT_T *type; /**< Type specific information for the elementary stream */ + + uint32_t bitrate; /**< Bitrate */ + + VC_CONTAINER_LANGUAGE_T language; /**< Language code for the elementary stream */ + uint32_t group_id; /**< ID of the group this elementary stream belongs to */ + + uint32_t flags; /**< Flags describing the properties of an elementary stream. + * See \ref esformatflags "Elementary stream format flags". */ + + unsigned int extradata_size; /**< Size of the codec specific data */ + uint8_t *extradata; /**< Codec specific data */ + +} VC_CONTAINER_ES_FORMAT_T; +/* @} */ + +/** \defgroup VcContainerPacket Container Packet + * A container packet is the unit of data that is being read from or written to a container */ +/* @{ */ + +/** Structure describing a data packet */ +typedef struct VC_CONTAINER_PACKET_T +{ + struct VC_CONTAINER_PACKET_T *next; /**< Used to build lists of packets */ + uint8_t *data; /**< Pointer to the buffer containing the actual data for the packet */ + unsigned int buffer_size; /**< Size of the p_data buffer. This is used to indicate how much data can be read in p_data */ + unsigned int size; /**< Size of the data contained in p_data */ + unsigned int frame_size; /**< If set, indicates the size of the frame this packet belongs to */ + int64_t pts; /**< Presentation Timestamp of the packet */ + int64_t dts; /**< Decoding Timestamp of the packet */ + uint64_t num; /**< Number of this packet */ + uint32_t track; /**< Track associated with this packet */ + uint32_t flags; /**< Flags associated with this packet */ + + void *user_data; /**< Field reserved for use by the client */ + void *framework_data; /**< Field reserved for use by the framework */ + +} VC_CONTAINER_PACKET_T; + +/** \name Container Packet Flags + * The following flags describe properties of the data packet */ +/* @{ */ +#define VC_CONTAINER_PACKET_FLAG_KEYFRAME 0x01 /**< Packet is a keyframe */ +#define VC_CONTAINER_PACKET_FLAG_FRAME_START 0x02 /**< Packet starts a frame */ +#define VC_CONTAINER_PACKET_FLAG_FRAME_END 0x04 /**< Packet ends a frame */ +#define VC_CONTAINER_PACKET_FLAG_FRAME 0x06 /**< Packet contains only complete frames */ +#define VC_CONTAINER_PACKET_FLAG_DISCONTINUITY 0x08 /**< Packet comes after a discontinuity in the stream. Decoders might have to be flushed */ +#define VC_CONTAINER_PACKET_FLAG_ENCRYPTED 0x10 /**< Packet contains DRM encrypted data */ +#define VC_CONTAINER_PACKET_FLAG_CONFIG 0x20 /**< Packet contains stream specific config data */ +/* @} */ + +/** \name Special Unknown Time Value + * This is the special value used to signal that a timestamp is not known */ +/* @{ */ +#define VC_CONTAINER_TIME_UNKNOWN (INT64_C(1)<<63) /**< Special value for signalling that time is not known */ +/* @} */ + +/* @} */ + +/** \name Track flags + * \anchor trackflags + * The following flags describe properties of a track */ +/* @{ */ +#define VC_CONTAINER_TRACK_FLAG_CHANGED 0x1 /**< Track definition has changed */ +/* @} */ + +/** Definition of the track type */ +typedef struct VC_CONTAINER_TRACK_T +{ + struct VC_CONTAINER_TRACK_PRIVATE_T *priv; /**< Private member used by the implementation */ + uint32_t is_enabled; /**< Flag to specify if the track is enabled */ + uint32_t flags; /**< Flags describing the properties of a track. + * See \ref trackflags "Track flags". */ + + VC_CONTAINER_ES_FORMAT_T *format; /**< Format of the elementary stream contained in the track */ + + unsigned int meta_num; /**< Number of metadata elements associated with the track */ + VC_CONTAINER_METADATA_T **meta; /**< Array of metadata elements associated with the track */ + +} VC_CONTAINER_TRACK_T; + +/** Definition of the DRM type */ +typedef struct VC_CONTAINER_DRM_T +{ + VC_CONTAINER_FOURCC_T format; /**< Four character code describing the format of the DRM in use */ + unsigned int views_max; /**< Maximum number of views allowed */ + unsigned int views_current; /**< Current number of views */ + +} VC_CONTAINER_DRM_T; + +/** Type definition for the progress reporting function. This function will be called regularly + * by the container during a call which blocks for too long and will report the progress of the + * operation as an estimated total length in microseconds and a percentage done. + * Returning anything else than VC_CONTAINER_SUCCESS in this function will abort the current + * operation. */ +typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_PROGRESS_REPORT_FUNC_T)(void *userdata, + int64_t length, unsigned int percentage_done); + +/** \name Container Events + * The following flags are exported by containers to notify the application of events */ +/* @{ */ +/** Type definition for container events */ +typedef uint32_t VC_CONTAINER_EVENTS_T; +#define VC_CONTAINER_EVENT_TRACKS_CHANGE 1 /**< Track information has changed */ +#define VC_CONTAINER_EVENT_METADATA_CHANGE 2 /**< Metadata has changed */ +/* @} */ + +/** Definition of the container context */ +typedef struct VC_CONTAINER_T +{ + struct VC_CONTAINER_PRIVATE_T *priv; /**< Private member used by the implementation */ + + VC_CONTAINER_EVENTS_T events; /**< Events generated by the container */ + VC_CONTAINER_CAPABILITIES_T capabilities; /**< Capabilities exported by the container */ + + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress; /**< Progress report function pointer */ + void *progress_userdata; /**< Progress report user data */ + + int64_t duration; /**< Duration of the media in microseconds */ + int64_t position; /**< Current time position into the media */ + int64_t size; /**< Size of the media in bytes */ + + unsigned int tracks_num; /**< Number of tracks available */ + /** Pointer to an array of pointers to track elements. + * The reasoning for using a pointer to pointers here is to allow us to extend + * VC_CONTAINER_TRACK_T without losing binary backward compatibility. */ + VC_CONTAINER_TRACK_T **tracks; + + unsigned int meta_num; /**< Number of metadata elements associated with the container */ + VC_CONTAINER_METADATA_T **meta; /**< Array of metadata elements associated with the container */ + + VC_CONTAINER_DRM_T *drm; /**< Description used for DRM protected content */ + +} VC_CONTAINER_T; + +/** Forward declaration of a container input / output context. + * This structure defines the context for a container io instance */ +typedef struct VC_CONTAINER_IO_T VC_CONTAINER_IO_T; + +/** Opens the media container pointed to by the URI for reading. + * This will create an an instance of a container reader and its associated context. + * The context returned will also be filled with the information retrieved from the media. + * + * If the media isn't accessible or recognized, this will return a null pointer as well as + * an error code indicating why this failed. + * + * \param psz_uri Unified Resource Identifier pointing to the media container + * \param status Returns the status of the operation + * \param pf_progress User provided function pointer to a progress report function. Can be set to + * null if no progress report is wanted. This function will be used during + * the whole lifetime of the instance (i.e. it will be used during + * open / seek / close) + * \param progress_userdata User provided pointer that will be passed during the progress report + * function call. + * \return A pointer to the context of the new instance of the + * container reader. Returns NULL on failure. + */ +VC_CONTAINER_T *vc_container_open_reader( const char *psz_uri, VC_CONTAINER_STATUS_T *status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata); + +/** Opens for reading the media container pointed to by the container i/o. + * This will create an an instance of a container reader and its associated context. + * The context returned will also be filled with the information retrieved from the media. + * + * If the media isn't accessible or recognized, this will return a null pointer as well as + * an error code indicating why this failed. + * + * \param p_io Instance of the container i/o to use + * \param psz_uri Unified Resource Identifier pointing to the media container (optional) + * \param status Returns the status of the operation + * \param pf_progress User provided function pointer to a progress report function. Can be set to + * null if no progress report is wanted. This function will be used during + * the whole lifetime of the instance (i.e. it will be used during + * open / seek / close) + * \param progress_userdata User provided pointer that will be passed during the progress report + * function call. + * \return A pointer to the context of the new instance of the + * container reader. Returns NULL on failure. + */ +VC_CONTAINER_T *vc_container_open_reader_with_io( VC_CONTAINER_IO_T *p_io, + const char *psz_uri, VC_CONTAINER_STATUS_T *status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata); + +/** Opens the media container pointed to by the URI for writing. + * This will create an an instance of a container writer and its associated context. + * The context returned will be initialised to sensible values. + * + * The application will need to add all the media tracks using \ref vc_container_control before + * it starts writing data using \ref vc_container_write. + * + * If the media isn't accessible or recognized, this will return a null pointer as well as + * an error code indicating why this failed. + * + * \param psz_uri Unified Resource Identifier pointing to the media container + * \param status Returns the status of the operation + * \param pf_progress User provided function pointer to a progess report function. Can be set to + * null if no progress report is wanted. + * \param progress_userdata User provided pointer that will be passed during the progress report + * function call. + * \return A pointer to the context of the new instance of the + * container writer. Returns NULL on failure. + */ +VC_CONTAINER_T *vc_container_open_writer( const char *psz_uri, VC_CONTAINER_STATUS_T *status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata); + +/** Closes an instance of a container reader / writer. + * This will free all the resources associated with the context. + * + * \param context Pointer to the context of the instance to close + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_close( VC_CONTAINER_T *context ); + +/** \name Container read flags + * The following flags can be passed during a read call */ +/* @{ */ +/** Type definition for the read flags */ +typedef uint32_t VC_CONTAINER_READ_FLAGS_T; +/** Ask the container to only return information on the next packet without reading it */ +#define VC_CONTAINER_READ_FLAG_INFO 1 +/** Ask the container to skip the next packet */ +#define VC_CONTAINER_READ_FLAG_SKIP 2 +/** Force the container to read data from the specified track */ +#define VC_CONTAINER_READ_FLAG_FORCE_TRACK 4 +/* @} */ + +/** Reads a data packet from a container reader. + * By default, the reader will read whatever packet comes next in the container and update the + * given \ref VC_CONTAINER_PACKET_T structure with this packet's information. + * This behaviour can be changed using the \ref VC_CONTAINER_READ_FLAGS_T.\n + * \ref VC_CONTAINER_READ_FLAG_INFO will instruct the reader to only return information on the + * following packet but not its actual data. The data can be retreived later by issuing another + * read request.\n + * \ref VC_CONTAINER_READ_FLAG_FORCE_TRACK will force the reader to read the next packet for the + * selected track (as present in the \ref VC_CONTAINER_PACKET_T structure) instead of defaulting + * to reading the packet which comes next in the container.\n + * \ref VC_CONTAINER_READ_FLAG_SKIP will instruct the reader to skip the next packet. In this case + * it isn't necessary for the caller to pass a pointer to a \ref VC_CONTAINER_PACKET_T structure + * unless the \ref VC_CONTAINER_READ_FLAG_INFO is also given.\n + * A combination of all these flags can be used. + * + * \param context Pointer to the context of the reader to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * This needs to be partially filled before the call (buffer, buffer_size) + * \param flags Flags controlling the read operation + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_read( VC_CONTAINER_T *context, + VC_CONTAINER_PACKET_T *packet, VC_CONTAINER_READ_FLAGS_T flags ); + +/** Writes a data packet to a container writer. + * + * \param context Pointer to the context of the writer to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_write( VC_CONTAINER_T *context, + VC_CONTAINER_PACKET_T *packet ); + +/** Definition of the different seek modes */ +typedef enum +{ + /** The offset provided for seeking is an absolute time offset in microseconds */ + VC_CONTAINER_SEEK_MODE_TIME = 0, + /** The offset provided for seeking is a percentage (Q32 ?) */ + VC_CONTAINER_SEEK_MODE_PERCENT + +} VC_CONTAINER_SEEK_MODE_T; + +/** \name Container Seek Flags + * The following flags control seek operations */ +/* @{ */ +/** Type definition for the seek flags */ +typedef uint32_t VC_CONTAINER_SEEK_FLAGS_T; +/** Choose precise seeking even if slower */ +#define VC_CONTAINER_SEEK_FLAG_PRECISE 0x1 +/** By default a seek will always seek to the keyframe which comes just before the requested + * position. This flag allows the caller to force the container to seek to the keyframe which + * comes just after the requested position. */ +#define VC_CONTAINER_SEEK_FLAG_FORWARD 0x2 +/* @} */ + +/** Seek into a container reader. + * + * \param context Pointer to the context of the reader to use + * \param offset Offset to seek to. Used as an input as well as output value. + * \param mode Seeking mode requested. + * \param flags Flags affecting the seeking operation. + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_seek( VC_CONTAINER_T *context, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags); + +/** Performance statistics. + */ +/** The maximum number of bins a statistics value is held in */ +#define VC_CONTAINER_STATS_BINS 10 + +/** This type is used to represent multiple values of a statistic. + */ +typedef struct VC_CONTAINER_STATS_T +{ + /** The number of places to right shift count before using. Resulting values + * of zero are rounded to 1. */ + uint32_t shift; + + /** We store VC_CONTAINER_STATS_BINS+1 records, in sorted order of numpc. + * At least one will be invalid and all zero. We combine adjacent records + * as necessary. */ + struct { + /** Sum of count. For single value statistics this is the freqency, for paired statistics + * this is the number of bytes written. */ + uint32_t count; + /** Number of count. For single value statistics this is the total value, for paired statistics + * this is the total length of time. */ + uint32_t num; + /** Number>>shift per count. Stored to save recalculation. */ + uint32_t numpc; + } record[VC_CONTAINER_STATS_BINS+1]; +} VC_CONTAINER_STATS_T; + +/** This type represents the statistics saved by the io layer. */ +typedef struct VC_CONTAINER_WRITE_STATS_T +{ + /** This logs the number of bytes written in count, and the microseconds taken to write + * in num. */ + VC_CONTAINER_STATS_T write; + /** This logs the length of time the write function has to wait for the asynchronous task. */ + VC_CONTAINER_STATS_T wait; + /** This logs the length of time that we wait for a flush command to complete. */ + VC_CONTAINER_STATS_T flush; +} VC_CONTAINER_WRITE_STATS_T; + + +/** Control operations which can be done on containers. */ +typedef enum +{ + /** Adds a new track to the list of tracks. This should be used by writers to create + * their list of tracks.\n + * Arguments:\n + * arg1= VC_CONTAINER_ES_FORMAT_T *: format of the track to add\n + * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */ + VC_CONTAINER_CONTROL_TRACK_ADD = 0, + + /** Specifies that we're done adding new tracks. This is optional but can be used by writers + * to trigger the writing of the container header early. If this isn't used, the header will be + * written when the first data packet is received.\n + * No arguments.\n + * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */ + VC_CONTAINER_CONTROL_TRACK_ADD_DONE, + + /** Change the format of a track in the list of tracks. This should be used by writers to modify + * the format of a track at run-time.\n + * Arguments:\n + * arg1= unsigned int: index of track to change\n + * arg2= VC_CONTAINER_ES_FORMAT_T *: format of the track to add\n + * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */ + VC_CONTAINER_CONTROL_TRACK_CHANGE, + + /** Deletes a track from the list of tracks. This should be used by writers to delete tracks + * during run-time. Note that vc_container_close will automatically delete all track so it + * isn't necessary to call this before closing a writer.\n + * Arguments:\n + * arg1= index of the track to delete */ + VC_CONTAINER_CONTROL_TRACK_DEL, + + /** Activate the playback of DRM protected content.\n + * No arguments.\n + * return= one of the VC_CONTAINER_ERROR_DRM error codes if content can't be played */ + VC_CONTAINER_CONTROL_DRM_PLAY, + + /** TBD */ + VC_CONTAINER_CONTROL_METADATA_ADD, + /** TBD */ + VC_CONTAINER_CONTROL_METADATA_CHANGE, + /** TBD */ + VC_CONTAINER_CONTROL_METADATA_DEL, + + /** TBD */ + VC_CONTAINER_CONTROL_CHAPTER_ADD, + /** TBD */ + VC_CONTAINER_CONTROL_CHAPTER_DEL, + + /** Set a maximum size for files generated by writers.\n + * Arguments:\n + * arg1= uint64_t: maximum size */ + VC_CONTAINER_CONTROL_SET_MAXIMUM_SIZE, + + /** Enables/disabled performance statistic gathering.\n + * Arguments:\n + * arg1= bool: enable or disable */ + VC_CONTAINER_CONTROL_SET_IO_PERF_STATS, + + /** Collects performance statistics.\n + * Arguments:\n + * arg1= VC_CONTAINER_WRITE_STATS_T *: */ + VC_CONTAINER_CONTROL_GET_IO_PERF_STATS, + + /** HACK.\n + * Arguments:\n + * arg1= void (*)(void *): callback function + * arg1= void *: opaque pointer to pass during the callback */ + VC_CONTAINER_CONTROL_SET_IO_BUFFER_FULL_CALLBACK, + + /** Set the I/O read buffer size to be used.\n + * Arguments:\n + * arg1= uint32_t: New buffer size in bytes*/ + VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE, + + /** Set the timeout on I/O read operations, if applicable.\n + * Arguments:\n + * arg1= uint32_t: New timeout in milliseconds, or VC_CONTAINER_READ_TIMEOUT_BLOCK */ + VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, + + /** Set the timestamp base.\n + * The timestamp passed equates to time zero for the stream.\n + * Arguments:\n + * arg1= uint32_t: Timestamp base in stream clock units. */ + VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE, + + /** Set the next expected sequence number for the stream.\n + * Arguments:\n + * arg1= uint32_t: Next expected sequence number. */ + VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER, + + /** Set the source ID for the container.\n + * Arguments:\n + * arg1= uint32_t: Source identifier. */ + VC_CONTAINER_CONTROL_SET_SOURCE_ID, + + /** Arguments:\n + * arg1= void *: metadata buffer + * arg2= unsigned long: length of metadata in bytes */ + VC_CONTAINER_CONTROL_GET_DRM_METADATA, + + /** Arguments:\n + * arg1= unsigned long: track number + * arg2= VC_CONTAINER_FOURCC_T : drm type + * arg3= void *: encryption configuration parameters. + * arg4= unsigned long: configuration data length */ + VC_CONTAINER_CONTROL_ENCRYPT_TRACK, + + /** Causes the io to be flushed.\n + * Arguments: none */ + VC_CONTAINER_CONTROL_IO_FLUSH, + + /** Request the container reader to packetize data for the specified track. + * Arguments:\n + * arg1= unsigned long: track number + * arg2= VC_CONTAINER_FOURCC_T: codec variant to output */ + VC_CONTAINER_CONTROL_TRACK_PACKETIZE, + + /** Private user extensions must be above this number */ + VC_CONTAINER_CONTROL_USER_EXTENSIONS = 0x1000 + +} VC_CONTAINER_CONTROL_T; + +/** Used with the VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS control to indicate the read shall + * block until either data is available, or an error occurs. + */ +#define VC_CONTAINER_READ_TIMEOUT_BLOCK (uint32_t)(-1) + +/** Extensible control function for container readers and writers. + * This function takes a variable number of arguments which will depend on the specific operation. + * + * \param context Pointer to the VC_CONTAINER_T context to use + * \param operation The requested operation + * \return the status of the operation. Can be \ref VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION + * if the operation is not supported or implemented by the container. + */ +VC_CONTAINER_STATUS_T vc_container_control( VC_CONTAINER_T *context, VC_CONTAINER_CONTROL_T operation, ... ); + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* VC_CONTAINERS_H */ diff --git a/containers/containers_codecs.h b/containers/containers_codecs.h new file mode 100755 index 0000000..3a6bc4b --- /dev/null +++ b/containers/containers_codecs.h @@ -0,0 +1,214 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_CODECS_H +#define VC_CONTAINERS_CODECS_H + +/** \file containers_codecs.h + * Codec helpers + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "containers/containers_types.h" + +/* Video */ +#define VC_CONTAINER_CODEC_MP1V VC_FOURCC('m','p','1','v') +#define VC_CONTAINER_CODEC_MP2V VC_FOURCC('m','p','2','v') +#define VC_CONTAINER_CODEC_MP4V VC_FOURCC('m','p','4','v') +#define VC_CONTAINER_CODEC_DIV3 VC_FOURCC('d','i','v','3') +#define VC_CONTAINER_CODEC_DIV4 VC_FOURCC('d','i','v','4') +#define VC_CONTAINER_CODEC_H263 VC_FOURCC('h','2','6','3') +#define VC_CONTAINER_CODEC_H264 VC_FOURCC('h','2','6','4') +#define VC_CONTAINER_CODEC_MVC VC_FOURCC('m','v','c',' ') +#define VC_CONTAINER_CODEC_WMV1 VC_FOURCC('w','m','v','1') +#define VC_CONTAINER_CODEC_WMV2 VC_FOURCC('w','m','v','2') +#define VC_CONTAINER_CODEC_WMV3 VC_FOURCC('w','m','v','3') +#define VC_CONTAINER_CODEC_WVC1 VC_FOURCC('w','v','c','1') +#define VC_CONTAINER_CODEC_WMVA VC_FOURCC('w','m','v','a') +#define VC_CONTAINER_CODEC_MJPEG VC_FOURCC('m','j','p','g') +#define VC_CONTAINER_CODEC_MJPEGA VC_FOURCC('m','j','p','a') +#define VC_CONTAINER_CODEC_MJPEGB VC_FOURCC('m','j','p','b') +#define VC_CONTAINER_CODEC_THEORA VC_FOURCC('t','h','e','o') +#define VC_CONTAINER_CODEC_VP3 VC_FOURCC('v','p','3',' ') +#define VC_CONTAINER_CODEC_VP6 VC_FOURCC('v','p','6',' ') +#define VC_CONTAINER_CODEC_VP7 VC_FOURCC('v','p','7',' ') +#define VC_CONTAINER_CODEC_VP8 VC_FOURCC('v','p','8',' ') +#define VC_CONTAINER_CODEC_RV10 VC_FOURCC('r','v','1','0') +#define VC_CONTAINER_CODEC_RV20 VC_FOURCC('r','v','2','0') +#define VC_CONTAINER_CODEC_RV30 VC_FOURCC('r','v','3','0') +#define VC_CONTAINER_CODEC_RV40 VC_FOURCC('r','v','4','0') +#define VC_CONTAINER_CODEC_AVS VC_FOURCC('a','v','s',' ') +#define VC_CONTAINER_CODEC_SPARK VC_FOURCC('s','p','r','k') +#define VC_CONTAINER_CODEC_DIRAC VC_FOURCC('d','r','a','c') + +#define VC_CONTAINER_CODEC_YUV VC_FOURCC('y','u','v',' ') +#define VC_CONTAINER_CODEC_I420 VC_FOURCC('I','4','2','0') +#define VC_CONTAINER_CODEC_YV12 VC_FOURCC('Y','V','1','2') +#define VC_CONTAINER_CODEC_I422 VC_FOURCC('I','4','2','2') +#define VC_CONTAINER_CODEC_YUYV VC_FOURCC('Y','U','Y','V') +#define VC_CONTAINER_CODEC_YVYU VC_FOURCC('Y','V','Y','U') +#define VC_CONTAINER_CODEC_UYVY VC_FOURCC('U','Y','V','Y') +#define VC_CONTAINER_CODEC_VYUY VC_FOURCC('V','Y','U','Y') +#define VC_CONTAINER_CODEC_NV12 VC_FOURCC('N','V','1','2') +#define VC_CONTAINER_CODEC_NV21 VC_FOURCC('N','V','2','1') +#define VC_CONTAINER_CODEC_ARGB VC_FOURCC('A','R','G','B') +#define VC_CONTAINER_CODEC_RGBA VC_FOURCC('R','G','B','A') +#define VC_CONTAINER_CODEC_ABGR VC_FOURCC('A','B','G','R') +#define VC_CONTAINER_CODEC_BGRA VC_FOURCC('B','G','R','A') +#define VC_CONTAINER_CODEC_RGB16 VC_FOURCC('R','G','B','2') +#define VC_CONTAINER_CODEC_RGB24 VC_FOURCC('R','G','B','3') +#define VC_CONTAINER_CODEC_RGB32 VC_FOURCC('R','G','B','4') +#define VC_CONTAINER_CODEC_BGR16 VC_FOURCC('B','G','R','2') +#define VC_CONTAINER_CODEC_BGR24 VC_FOURCC('B','G','R','3') +#define VC_CONTAINER_CODEC_BGR32 VC_FOURCC('B','G','R','4') +#define VC_CONTAINER_CODEC_YUVUV128 VC_FOURCC('S','A','N','D') + +#define VC_CONTAINER_CODEC_JPEG VC_FOURCC('j','p','e','g') +#define VC_CONTAINER_CODEC_PNG VC_FOURCC('p','n','g',' ') +#define VC_CONTAINER_CODEC_GIF VC_FOURCC('g','i','f',' ') +#define VC_CONTAINER_CODEC_PPM VC_FOURCC('p','p','m',' ') +#define VC_CONTAINER_CODEC_TGA VC_FOURCC('t','g','a',' ') +#define VC_CONTAINER_CODEC_BMP VC_FOURCC('b','m','p',' ') + +/* Audio */ +#define VC_CONTAINER_CODEC_PCM_UNSIGNED_BE VC_FOURCC('P','C','M','U') +#define VC_CONTAINER_CODEC_PCM_UNSIGNED_LE VC_FOURCC('p','c','m','u') +#define VC_CONTAINER_CODEC_PCM_SIGNED_BE VC_FOURCC('P','C','M','S') +#define VC_CONTAINER_CODEC_PCM_SIGNED_LE VC_FOURCC('p','c','m','s') +#define VC_CONTAINER_CODEC_PCM_FLOAT_BE VC_FOURCC('P','C','M','F') +#define VC_CONTAINER_CODEC_PCM_FLOAT_LE VC_FOURCC('p','c','m','f') +/* Defines for native endianness */ +#ifdef VC_CONTAINER_IS_BIG_ENDIAN +#define VC_CONTAINER_CODEC_PCM_UNSIGNED VC_CONTAINER_CODEC_PCM_UNSIGNED_BE +#define VC_CONTAINER_CODEC_PCM_SIGNED VC_CONTAINER_CODEC_PCM_SIGNED_BE +#define VC_CONTAINER_CODEC_PCM_FLOAT VC_CONTAINER_CODEC_PCM_FLOAT_BE +#else +#define VC_CONTAINER_CODEC_PCM_UNSIGNED VC_CONTAINER_CODEC_PCM_UNSIGNED_LE +#define VC_CONTAINER_CODEC_PCM_SIGNED VC_CONTAINER_CODEC_PCM_SIGNED_LE +#define VC_CONTAINER_CODEC_PCM_FLOAT VC_CONTAINER_CODEC_PCM_FLOAT_LE +#endif + +#define VC_CONTAINER_CODEC_MPGA VC_FOURCC('m','p','g','a') +#define VC_CONTAINER_CODEC_MP4A VC_FOURCC('m','p','4','a') +#define VC_CONTAINER_CODEC_ALAW VC_FOURCC('a','l','a','w') +#define VC_CONTAINER_CODEC_MULAW VC_FOURCC('u','l','a','w') +#define VC_CONTAINER_CODEC_ADPCM_MS VC_FOURCC('m','s',0x0,0x2) +#define VC_CONTAINER_CODEC_ADPCM_IMA_MS VC_FOURCC('m','s',0x0,0x1) +#define VC_CONTAINER_CODEC_ADPCM_SWF VC_FOURCC('a','s','w','f') +#define VC_CONTAINER_CODEC_WMA1 VC_FOURCC('w','m','a','1') +#define VC_CONTAINER_CODEC_WMA2 VC_FOURCC('w','m','a','2') +#define VC_CONTAINER_CODEC_WMAP VC_FOURCC('w','m','a','p') +#define VC_CONTAINER_CODEC_WMAL VC_FOURCC('w','m','a','l') +#define VC_CONTAINER_CODEC_WMAV VC_FOURCC('w','m','a','v') +#define VC_CONTAINER_CODEC_AMRNB VC_FOURCC('a','m','r','n') +#define VC_CONTAINER_CODEC_AMRWB VC_FOURCC('a','m','r','w') +#define VC_CONTAINER_CODEC_AMRWBP VC_FOURCC('a','m','r','p') +#define VC_CONTAINER_CODEC_AC3 VC_FOURCC('a','c','3',' ') +#define VC_CONTAINER_CODEC_EAC3 VC_FOURCC('e','a','c','3') +#define VC_CONTAINER_CODEC_DTS VC_FOURCC('d','t','s',' ') +#define VC_CONTAINER_CODEC_MLP VC_FOURCC('m','l','p',' ') +#define VC_CONTAINER_CODEC_FLAC VC_FOURCC('f','l','a','c') +#define VC_CONTAINER_CODEC_VORBIS VC_FOURCC('v','o','r','b') +#define VC_CONTAINER_CODEC_SPEEX VC_FOURCC('s','p','x',' ') +#define VC_CONTAINER_CODEC_ATRAC3 VC_FOURCC('a','t','r','3') +#define VC_CONTAINER_CODEC_ATRACX VC_FOURCC('a','t','r','x') +#define VC_CONTAINER_CODEC_ATRACL VC_FOURCC('a','t','r','l') +#define VC_CONTAINER_CODEC_MIDI VC_FOURCC('m','i','d','i') +#define VC_CONTAINER_CODEC_EVRC VC_FOURCC('e','v','r','c') +#define VC_CONTAINER_CODEC_NELLYMOSER VC_FOURCC('n','e','l','y') +#define VC_CONTAINER_CODEC_QCELP VC_FOURCC('q','c','e','l') + +/* Text */ +#define VC_CONTAINER_CODEC_TEXT VC_FOURCC('t','e','x','t') +#define VC_CONTAINER_CODEC_SSA VC_FOURCC('s','s','a',' ') +#define VC_CONTAINER_CODEC_USF VC_FOURCC('u','s','f',' ') +#define VC_CONTAINER_CODEC_VOBSUB VC_FOURCC('v','s','u','b') + +#define VC_CONTAINER_CODEC_UNKNOWN VC_FOURCC('u','n','k','n') + +/* Codec variants */ + +/** ISO 14496-10 Annex B byte stream format */ +#define VC_CONTAINER_VARIANT_H264_DEFAULT 0 +/** ISO 14496-15 AVC format (used in mp4/mkv and other containers) */ +#define VC_CONTAINER_VARIANT_H264_AVC1 VC_FOURCC('a','v','c','C') +/** Implicitly delineated NAL units without emulation prevention */ +#define VC_CONTAINER_VARIANT_H264_RAW VC_FOURCC('r','a','w',' ') + +/** MPEG 1/2 Audio - Layer unknown */ +#define VC_CONTAINER_VARIANT_MPGA_DEFAULT 0 +/** MPEG 1/2 Audio - Layer 1 */ +#define VC_CONTAINER_VARIANT_MPGA_L1 VC_FOURCC('l','1',' ',' ') +/** MPEG 1/2 Audio - Layer 2 */ +#define VC_CONTAINER_VARIANT_MPGA_L2 VC_FOURCC('l','2',' ',' ') +/** MPEG 1/2 Audio - Layer 3 */ +#define VC_CONTAINER_VARIANT_MPGA_L3 VC_FOURCC('l','3',' ',' ') + +/** Converts a WaveFormat ID into a VC_CONTAINER_FOURCC_T. + * + * \param waveformat_id WaveFormat ID to convert + * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found. + */ +VC_CONTAINER_FOURCC_T waveformat_to_codec(uint16_t waveformat_id); + +/** Converts a VC_CONTAINER_FOURCC_T into a WaveFormat ID. + * + * \param codec VC_CONTAINER_FOURCC_T to convert + * \return a valid WaveFormat ID of 0 if no mapping was found. + */ +uint16_t codec_to_waveformat(VC_CONTAINER_FOURCC_T codec); + +/** Tries to convert a generic fourcc into a VC_CONTAINER_FOURCC_T. + * + * \param fourcc fourcc to convert + * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found. + */ +VC_CONTAINER_FOURCC_T fourcc_to_codec(uint32_t fourcc); + +uint32_t codec_to_fourcc(VC_CONTAINER_FOURCC_T codec); + +/** Tries to convert VideoForWindows fourcc into a VC_CONTAINER_FOURCC_T. + * + * \param fourcc vfw fourcc to convert + * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found. + */ +VC_CONTAINER_FOURCC_T vfw_fourcc_to_codec(uint32_t fourcc); + +/** Tries to convert a VC_CONTAINER_FOURCC_T into a VideoForWindows fourcc. + * + * \param codec VC_CONTAINER_FOURCC_T to convert + * \return a valid vfw fourcc or 0 if no mapping was found. + */ +uint32_t codec_to_vfw_fourcc(VC_CONTAINER_FOURCC_T codec); + +#ifdef __cplusplus +} +#endif + +#endif /* VC_CONTAINERS_CODECS_H */ diff --git a/containers/containers_types.h b/containers/containers_types.h new file mode 100755 index 0000000..4cf6666 --- /dev/null +++ b/containers/containers_types.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_TYPES_H +#define VC_CONTAINERS_TYPES_H + +/** \file containers_types.h + * Definition of types used by the containers API + */ + +#include +#include +#include +#include + +#if defined( __STDC__ ) && defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +#include + +#elif defined( __GNUC__ ) +#include +#include + +#elif defined(_MSC_VER) +#include +#if (_MSC_VER < 1700) +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#endif +#define PRIu16 "u" +#define PRIu32 "u" +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" + +#else +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed long int32_t; +typedef unsigned long uint32_t; +typedef signed long long int64_t; +typedef unsigned long long uint64_t; +#endif + +/* C99 64bits integers */ +#ifndef INT64_C +# define INT64_C(value) value##LL +# define UINT64_C(value) value##ULL +#endif + +/* C99 boolean */ +#ifndef __cplusplus +#ifndef bool +# define bool int +#endif +#ifndef true +# define true 1 +#endif +#ifndef false +# define false 0 +#endif +#endif /* __cplusplus */ + +/* FIXME: should be different for big endian */ +#define VC_FOURCC(a,b,c,d) ((a) | (b << 8) | (c << 16) | (d << 24)) + +#endif /* VC_CONTAINERS_TYPES_H */ diff --git a/containers/core/containers.c b/containers/core/containers.c new file mode 100755 index 0000000..3693e35 --- /dev/null +++ b/containers/core/containers.c @@ -0,0 +1,637 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_filters.h" +#include "containers/core/containers_loader.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" + +#define WRITER_SPACE_SAFETY_MARGIN (10*1024) +#define PACKETIZER_BUFFER_SIZE (32*1024) + +/*****************************************************************************/ +VC_CONTAINER_T *vc_container_open_reader_with_io( struct VC_CONTAINER_IO_T *io, + const char *uri, VC_CONTAINER_STATUS_T *p_status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_T *p_ctx = 0; + const char *extension; + + VC_CONTAINER_PARAM_UNUSED(pfn_progress); + VC_CONTAINER_PARAM_UNUSED(progress_userdata); + VC_CONTAINER_PARAM_UNUSED(uri); + + /* Sanity check the i/o */ + if (!io || !io->pf_read || !io->pf_seek) + { + LOG_ERROR(0, "invalid i/o instance: %p", io); + status = VC_CONTAINER_ERROR_INVALID_ARGUMENT; + goto error; + } + + /* Allocate our context before trying out the different readers / writers */ + p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->drm)); + if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->drm)); + p_ctx->priv = (VC_CONTAINER_PRIVATE_T *)(p_ctx + 1); + p_ctx->priv->verbosity = vc_container_log_get_default_verbosity(); + p_ctx->drm = (VC_CONTAINER_DRM_T *)(p_ctx->priv + 1); + p_ctx->size = io->size; + p_ctx->priv->io = io; + p_ctx->priv->uri = io->uri_parts; + + /* If the uri has an extension, use it as a hint when loading the container */ + extension = vc_uri_path_extension(p_ctx->priv->uri); + /* If the user has specified a container, then use that instead */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + status = vc_container_load_reader(p_ctx, extension); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + p_ctx->priv->drm_filter = vc_container_filter_open(VC_FOURCC('d','r','m',' '), + VC_FOURCC('u','n','k','n'), p_ctx, &status); + if (status != VC_CONTAINER_SUCCESS) + { + /* Some other problem occurred aside from the filter not being appropriate or + the stream not needing it. */ + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + + /* Report no DRM and continue as normal */ + p_ctx->drm = NULL; + status = VC_CONTAINER_SUCCESS; + } + +end: + if(p_status) *p_status = status; + return p_ctx; + +error: + if (p_ctx) + { + p_ctx->priv->io = NULL; /* The i/o doesn't belong to us */ + vc_container_close(p_ctx); + p_ctx = NULL; + } + goto end; +} + +/*****************************************************************************/ +VC_CONTAINER_T *vc_container_open_reader( const char *uri, VC_CONTAINER_STATUS_T *p_status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata) +{ + VC_CONTAINER_IO_T *io; + VC_CONTAINER_T *ctx; + + /* Start by opening the URI */ + io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_READ, p_status ); + if (!io) + return 0; + + ctx = vc_container_open_reader_with_io( io, uri, p_status, pfn_progress, progress_userdata); + if (!ctx) + vc_container_io_close(io); + return ctx; +} + +/*****************************************************************************/ +VC_CONTAINER_T *vc_container_open_writer( const char *uri, VC_CONTAINER_STATUS_T *p_status, + VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_T *p_ctx = 0; + VC_CONTAINER_IO_T *io; + const char *extension; + VC_CONTAINER_PARAM_UNUSED(pfn_progress); + VC_CONTAINER_PARAM_UNUSED(progress_userdata); + + /* Start by opening the URI */ + io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_WRITE, &status ); + if(!io) goto error; + + /* Make sure we have enough space available to start writing */ + if(io->max_size && io->max_size < WRITER_SPACE_SAFETY_MARGIN) + { + LOG_DEBUG(p_ctx, "not enough space available to open a writer"); + status = VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + goto error; + } + + /* Allocate our context before trying out the different readers / writers */ + p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv)); + if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv)); + p_ctx->priv = (VC_CONTAINER_PRIVATE_T *)(p_ctx + 1); + p_ctx->priv->verbosity = vc_container_log_get_default_verbosity(); + p_ctx->priv->io = io; + p_ctx->priv->uri = io->uri_parts; + io = NULL; /* io now owned by the context */ + + /* If the uri has an extension, use it as a hint when loading the container */ + extension = vc_uri_path_extension(p_ctx->priv->uri); + /* If the user has specified a container, then use that instead */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + status = vc_container_load_writer(p_ctx, extension); + if(status != VC_CONTAINER_SUCCESS) goto error; + + end: + if(p_status) *p_status = status; + return p_ctx; + +error: + if(io) vc_container_io_close(io); + if (p_ctx) vc_container_close(p_ctx); + p_ctx = NULL; + goto end; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_close( VC_CONTAINER_T *p_ctx ) +{ + unsigned int i; + + if(!p_ctx) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->priv->packetizer) + vc_packetizer_close(p_ctx->tracks[i]->priv->packetizer); + if(p_ctx->priv->packetizer_buffer) free(p_ctx->priv->packetizer_buffer); + if(p_ctx->priv->drm_filter) vc_container_filter_close(p_ctx->priv->drm_filter); + if(p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx); + if(p_ctx->priv->io) vc_container_io_close(p_ctx->priv->io); + if(p_ctx->priv->module_handle) vc_container_unload(p_ctx); + for(i = 0; i < p_ctx->meta_num; i++) free(p_ctx->meta[i]); + if(p_ctx->meta_num) free(p_ctx->meta); + p_ctx->meta_num = 0; + free(p_ctx); + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T container_read_packet( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_STATUS_T status; + + while(1) + { + status = p_ctx->priv->pf_read(p_ctx, p_packet, flags); + if(status == VC_CONTAINER_ERROR_CONTINUE) + continue; + + if(!p_packet || (flags & VC_CONTAINER_READ_FLAG_SKIP)) + return status; /* We've just been requested to skip the data */ + + if(status != VC_CONTAINER_SUCCESS) + return status; + + /* Skip data from out of bounds tracks, disabled tracks or packets that are encrypted + and cannot be decrypted */ + if(p_packet->track >= p_ctx->tracks_num || + !p_ctx->tracks[p_packet->track]->is_enabled || + ((p_packet->flags & VC_CONTAINER_PACKET_FLAG_ENCRYPTED) && !p_ctx->priv->drm_filter)) + { + if(flags & VC_CONTAINER_READ_FLAG_INFO) + status = p_ctx->priv->pf_read(p_ctx, p_packet, VC_CONTAINER_READ_FLAG_SKIP); + if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_CONTINUE) + continue; + } + if(status != VC_CONTAINER_SUCCESS) + return status; + + if(p_ctx->priv->drm_filter) + status = vc_container_filter_process(p_ctx->priv->drm_filter, p_packet); + + break; + } + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_CONTINUE; + VC_PACKETIZER_FLAGS_T packetizer_flags = 0; + VC_PACKETIZER_T *packetizer; + uint32_t force = flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK; + unsigned int i; + + if(!p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + if(!p_packet && (flags & VC_CONTAINER_READ_FLAG_INFO)) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + if(p_packet && !p_packet->data && !(flags & (VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_SKIP))) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && + (!p_packet || p_packet->track >= p_ctx->tracks_num || !p_ctx->tracks[p_packet->track]->is_enabled)) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + /* Always having a packet structure to work with simplifies things */ + if(!p_packet) + p_packet = &p_ctx->priv->packetizer_packet; + + /* Simple/Fast case first */ + if(!p_ctx->priv->packetizing) + { + status = container_read_packet( p_ctx, p_packet, flags ); + goto end; + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + packetizer_flags |= VC_PACKETIZER_FLAG_INFO; + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + packetizer_flags |= VC_PACKETIZER_FLAG_SKIP; + + /* Loop through all the packetized tracks first to see if we've got any + * data to consume there */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_PACKETIZER_T *packetizer = p_ctx->tracks[i]->priv->packetizer; + if(!p_ctx->tracks[i]->is_enabled || !packetizer || + (force && i != p_packet->track)) + continue; + + status = vc_packetizer_read(packetizer, p_packet, packetizer_flags); + p_packet->track = i; + if(status == VC_CONTAINER_SUCCESS) + break; + } + if(i < p_ctx->tracks_num) /* We've got some data */ + goto end; + + /* Let's go and read some data from the actual container */ + while(1) + { + VC_CONTAINER_PACKET_T *tmp = &p_ctx->priv->packetizer_packet; + tmp->track = p_packet->track; + + /* Let's check what the container has to offer */ + status = container_read_packet( p_ctx, tmp, force|VC_PACKETIZER_FLAG_INFO ); + if(status != VC_CONTAINER_SUCCESS) + return status; + + if(!p_ctx->tracks[tmp->track]->priv->packetizer) + { + status = container_read_packet( p_ctx, p_packet, flags ); + break; + } + + /* Feed data from the container onto the packetizer */ + packetizer = p_ctx->tracks[tmp->track]->priv->packetizer; + + tmp->data = p_ctx->priv->packetizer_buffer; + tmp->buffer_size = PACKETIZER_BUFFER_SIZE; + tmp->size = 0; + status = container_read_packet( p_ctx, tmp, force ); + if(status != VC_CONTAINER_SUCCESS) + return status; + + p_packet->track = tmp->track; + vc_packetizer_push(packetizer, tmp); + vc_packetizer_pop(packetizer, &tmp, VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT); + + status = vc_packetizer_read(packetizer, p_packet, packetizer_flags); + if(status == VC_CONTAINER_SUCCESS) + break; + } + + end: + if(status != VC_CONTAINER_SUCCESS) + return status; + + if(p_packet && p_packet->dts > p_ctx->position) + p_ctx->position = p_packet->dts; + if(p_packet && p_packet->pts > p_ctx->position) + p_ctx->position = p_packet->pts; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_write( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet ) +{ + VC_CONTAINER_STATUS_T status; + void * p_metadata_buffer = NULL; + uint32_t metadata_length = 0; + + /* TODO: check other similar argument errors and non-stateless errors */ + if (!p_packet || !p_packet->data || p_packet->track >= p_ctx->tracks_num) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + /* Check for a previous error */ + if(p_ctx->priv->status != VC_CONTAINER_SUCCESS && p_ctx->priv->status != VC_CONTAINER_ERROR_NOT_READY) + return p_ctx->priv->status; + + /* Check we have enough space to write the data */ + if(p_ctx->priv->max_size && + p_ctx->size + p_packet->size + WRITER_SPACE_SAFETY_MARGIN > p_ctx->priv->max_size) + {status = VC_CONTAINER_ERROR_LIMIT_REACHED; goto end;} + if(p_ctx->priv->io->max_size && + p_ctx->size + p_packet->size + WRITER_SPACE_SAFETY_MARGIN + + (p_ctx->priv->tmp_io ? p_ctx->priv->tmp_io->offset : 0) > p_ctx->priv->io->max_size) + {status = VC_CONTAINER_ERROR_OUT_OF_SPACE; goto end;} + + /* If a filter is created, then send the packet to the filter for encryption. */ + if(p_ctx->priv->drm_filter) + { + status = vc_container_filter_process(p_ctx->priv->drm_filter, p_packet); + + if(status == VC_CONTAINER_SUCCESS) + { + /* Get the encryption metadata and send it to the output first. */ + if(vc_container_control(p_ctx, VC_CONTAINER_CONTROL_GET_DRM_METADATA, + &p_metadata_buffer, &metadata_length) == VC_CONTAINER_SUCCESS && metadata_length > 0) + { + /* Make a packet up with the metadata in the payload and write it. */ + VC_CONTAINER_PACKET_T metadata_packet; + metadata_packet.data = p_metadata_buffer; + metadata_packet.buffer_size = metadata_length; + metadata_packet.size = metadata_length; + metadata_packet.frame_size = p_packet->frame_size + metadata_length; + metadata_packet.pts = p_packet->pts; + metadata_packet.dts = p_packet->dts; + metadata_packet.num = p_packet->num; + metadata_packet.track = p_packet->track; + /* As this packet is written first, we must transfer any frame start + flag from the following packet. Also, this packet can never have the end flag set. */ + metadata_packet.flags = p_packet->flags & ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + p_packet->pts = p_packet->dts = VC_CONTAINER_TIME_UNKNOWN; + p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_START; + if(p_ctx->priv->pf_write(p_ctx, &metadata_packet) != VC_CONTAINER_SUCCESS) goto end; + } + } + else if (status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) + { + /* Encryption was appropriate but a problem has occurred. Skip the write of data + to the io and return the status to the caller. */ + goto end; + } + } + + status = p_ctx->priv->pf_write(p_ctx, p_packet); + + end: + p_ctx->priv->status = status; + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_STATUS_T status; + int64_t offset = *p_offset; + unsigned int i; + + /* Reset all packetizers */ + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->priv->packetizer) + vc_packetizer_reset(p_ctx->tracks[i]->priv->packetizer); + + status = p_ctx->priv->pf_seek(p_ctx, p_offset, mode, flags); + + /* */ + if(status == VC_CONTAINER_SUCCESS && (flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && + *p_offset < offset) + { + LOG_DEBUG(p_ctx, "container didn't seek forward as requested (%"PRIi64"/%"PRIi64")", + *p_offset, offset); + for(i = 1; i <= 5 && *p_offset < offset; i++) + { + *p_offset = offset + i * i * 100000; + status = p_ctx->priv->pf_seek(p_ctx, p_offset, mode, flags); + if(status != VC_CONTAINER_SUCCESS) break; + } + } + + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, ... ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + va_list args; + + va_start( args, operation ); + + if(operation == VC_CONTAINER_CONTROL_ENCRYPT_TRACK) + { + VC_CONTAINER_FOURCC_T type = va_arg(args, VC_CONTAINER_FOURCC_T); + + uint32_t track_num = va_arg(args, uint32_t); + void *p_drm_data = va_arg(args, void *); + int drm_data_size = va_arg(args, uint32_t); + + if(!p_ctx->priv->drm_filter && track_num < p_ctx->tracks_num) + { + VC_CONTAINER_TRACK_T * p_track = p_ctx->tracks[track_num]; + + if ((status = vc_container_track_allocate_drmdata(p_ctx, p_track, drm_data_size)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "failed to allocate memory for 'drm data' (%d bytes)", drm_data_size); + goto end; + } + memcpy(p_track->priv->drmdata, p_drm_data, drm_data_size); + + /* Track encryption enabled and no filter - create one now. */ + p_ctx->priv->drm_filter = vc_container_filter_open(VC_FOURCC('d','r','m',' '), type, p_ctx, &status); + if(status != VC_CONTAINER_SUCCESS) + goto end; + + status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, track_num); + } + } + else if(operation == VC_CONTAINER_CONTROL_GET_DRM_METADATA) + { + void *p_drm_data = va_arg(args, void *); + int *drm_data_size = va_arg(args, int *); + status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, p_drm_data, drm_data_size); + } + + if(status == VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION && p_ctx->priv->pf_control) + status = p_ctx->priv->pf_control(p_ctx, operation, args); + + /* If the request has already been handled then we're done */ + if(status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) + goto end; + + switch(operation) + { + case VC_CONTAINER_CONTROL_DRM_PLAY: + if (p_ctx->priv->drm_filter) + status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, args); + break; + + case VC_CONTAINER_CONTROL_SET_MAXIMUM_SIZE: + p_ctx->priv->max_size = (uint64_t)va_arg( args, uint64_t ); + if(p_ctx->priv->io->max_size && + p_ctx->priv->max_size > p_ctx->priv->io->max_size) + p_ctx->priv->max_size = p_ctx->priv->io->max_size; + status = VC_CONTAINER_SUCCESS; + break; + + case VC_CONTAINER_CONTROL_TRACK_PACKETIZE: + { + unsigned int track_num = va_arg(args, unsigned int); + VC_CONTAINER_FOURCC_T fourcc = va_arg(args, VC_CONTAINER_FOURCC_T); + VC_CONTAINER_TRACK_T *p_track; + + if(track_num >= p_ctx->tracks_num) + { + status = VC_CONTAINER_ERROR_INVALID_ARGUMENT; + break; + } + if(p_ctx->tracks[track_num]->priv->packetizer) + { + status = VC_CONTAINER_SUCCESS; + break; + } + + p_track = p_ctx->tracks[track_num]; + p_track->priv->packetizer = vc_packetizer_open( p_track->format, fourcc, &status ); + if(!p_track->priv->packetizer) + break; + + if(!p_ctx->priv->packetizer_buffer) + { + p_ctx->priv->packetizer_buffer = malloc(PACKETIZER_BUFFER_SIZE); + if(!p_ctx->priv->packetizer_buffer) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + vc_packetizer_close(p_track->priv->packetizer); + p_track->priv->packetizer = NULL; + break; + } + } + + vc_container_format_copy(p_track->format, p_track->priv->packetizer->out, + p_track->format->extradata_size); + p_track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + p_ctx->priv->packetizing = true; + } + break; + + default: break; + } + + if (status == VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) + status = vc_container_io_control_list(p_ctx->priv->io, operation, args); + + end: + va_end( args ); + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_TRACK_T *vc_container_allocate_track( VC_CONTAINER_T *context, unsigned int extra_size ) +{ + VC_CONTAINER_TRACK_T *p_ctx; + unsigned int size; + VC_CONTAINER_PARAM_UNUSED(context); + + size = sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->format) + + sizeof(*p_ctx->format->type) + extra_size; + + p_ctx = malloc(size); + if(!p_ctx) return 0; + + memset(p_ctx, 0, size); + p_ctx->priv = (VC_CONTAINER_TRACK_PRIVATE_T *)(p_ctx + 1); + p_ctx->format = (VC_CONTAINER_ES_FORMAT_T *)(p_ctx->priv + 1); + p_ctx->format->type = (void *)(p_ctx->format + 1); + p_ctx->priv->module = (struct VC_CONTAINER_TRACK_MODULE_T *)(p_ctx->format->type + 1); + return p_ctx; +} + +/*****************************************************************************/ +void vc_container_free_track( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *p_track ) +{ + VC_CONTAINER_PARAM_UNUSED(context); + if(p_track) + { + if(p_track->priv->extradata) free(p_track->priv->extradata); + if(p_track->priv->drmdata) free(p_track->priv->drmdata); + free(p_track); + } +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_track_allocate_extradata( VC_CONTAINER_T *context, + VC_CONTAINER_TRACK_T *p_track, unsigned int extra_size ) +{ + VC_CONTAINER_PARAM_UNUSED(context); + + /* Sanity check the size of the extra data */ + if(extra_size > 100*1024) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* Check if we need to allocate a buffer */ + if(extra_size > p_track->priv->extradata_size) + { + p_track->priv->extradata_size = 0; + if(p_track->priv->extradata) free(p_track->priv->extradata); + p_track->priv->extradata = malloc(extra_size); + p_track->format->extradata = p_track->priv->extradata; + if(!p_track->priv->extradata) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + p_track->priv->extradata_size = extra_size; + } + p_track->format->extradata = p_track->priv->extradata; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_track_allocate_drmdata( VC_CONTAINER_T *context, + VC_CONTAINER_TRACK_T *p_track, unsigned int size ) +{ + VC_CONTAINER_PARAM_UNUSED(context); + + /* Sanity check the size of the drm data */ + if(size > 200*1024) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* Check if we need to allocate a buffer */ + if(size > p_track->priv->drmdata_size) + { + p_track->priv->drmdata_size = 0; + if(p_track->priv->drmdata) free(p_track->priv->drmdata); + p_track->priv->drmdata = malloc(size); + if(!p_track->priv->drmdata) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + p_track->priv->drmdata_size = size; + } + + return VC_CONTAINER_SUCCESS; +} diff --git a/containers/core/containers_bits.c b/containers/core/containers_bits.c new file mode 100755 index 0000000..30f8650 --- /dev/null +++ b/containers/core/containers_bits.c @@ -0,0 +1,490 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "containers/core/containers_bits.h" +#include "containers/core/containers_common.h" + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT +#include "containers/core/containers_logging.h" +#endif + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT +/** String used for indentation. If more spaces are needed, just add them. */ +#define INDENT_SPACES_STRING "> " +#define INDENT_SPACES_LENGTH (sizeof(INDENT_SPACES_STRING) - 1) +#endif /* ENABLE_CONTAINERS_LOG_FORMAT */ + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/****************************************************************************** +Function prototypes +******************************************************************************/ + +/****************************************************************************** +Local Functions +******************************************************************************/ + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT + +/**************************************************************************//** + * Returns a string that indicates whether the bit stream is valid or not. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return A string indicating the validity of the stream. + */ +static const char * vc_container_bits_valid_str( VC_CONTAINER_BITS_T *bit_stream ) +{ + return vc_container_bits_valid(bit_stream) ? "" : " - stream invalid"; +} + +/**************************************************************************//** + * Returns a string of spaces the length of which is determined by the + * parameter. + * The length is limited to a certain size, above which a greater than symbol + * prefixes the maximum number of spaces. + * + * \param length The required length of the string. + * \return A string indicating the validity of the stream. + */ +static const char * vc_container_bits_indent_str(uint32_t length) +{ + uint32_t str_length = length; + + if (str_length > INDENT_SPACES_LENGTH) + str_length = INDENT_SPACES_LENGTH; + + return INDENT_SPACES_STRING + (INDENT_SPACES_LENGTH - str_length); +} + +#endif /* ENABLE_CONTAINERS_LOG_FORMAT */ + +/**************************************************************************//** + * Returns the number of consecutive zero bits in the stream. + * the zero bits are terminated either by a one bit, or the end of the stream. + * In the former case, the zero bits and the terminating one bit are removed + * from the stream. + * In the latter case, the stream becomes invalid. The stream also becomes + * invalid if there are not as many bits after the one bit as zero bits before + * it. + * If the stream is already or becomes invalid, zero is returned. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return The number of consecutive zero bits, or zero if the stream is + * invalid. + */ +static uint32_t vc_container_bits_get_leading_zero_bits( VC_CONTAINER_BITS_T *bit_stream ) +{ + uint32_t leading_zero_bits; + uint32_t bits_left = vc_container_bits_available(bit_stream); + uint32_t bits; + uint8_t mask, current_byte; + + if (!bits_left) + return vc_container_bits_invalidate(bit_stream); + + /* Cache 'bits' field to avoid repeated pointer access */ + bits = bit_stream->bits; + if (bits) + { + current_byte = *bit_stream->buffer; + mask = 1 << (bits - 1); + } else { + /* Initialize variables to placate the compiler */ + current_byte = 0; + mask = 0; + } + + /* Scan for the first one bit, counting the number of zeroes. This gives the + * number of further bits after the one that are part of the value. See + * section 9.1 of ITU-T REC H.264 201003 for more details. */ + + for (leading_zero_bits = 0; leading_zero_bits < bits_left; leading_zero_bits++) + { + if (!bits) + { + if (!bit_stream->bytes) + return vc_container_bits_invalidate(bit_stream); + bit_stream->bytes--; + current_byte = *(++bit_stream->buffer); + bits = 8; + mask = 0x80; + } + + bits--; + bits_left--; + if (current_byte & mask) + break; /* Found the marker bit */ + + mask >>= 1; + } + + /* Check enough bits are left in the stream for the value. */ + if (leading_zero_bits > bits_left) + return vc_container_bits_invalidate(bit_stream); + + /* Return cached value of bits to the stream */ + bit_stream->bits = bits; + + return leading_zero_bits; +} + +/***************************************************************************** +Functions exported as part of the bit stream API + *****************************************************************************/ + +/*****************************************************************************/ +void vc_container_bits_init(VC_CONTAINER_BITS_T *bit_stream, + const uint8_t *buffer, + uint32_t available) +{ + vc_container_assert(buffer && (buffer != (const uint8_t *)1)); + + /* Start with buffer pointing at the previous byte with no bits available + * to make the mathematics easier */ + bit_stream->buffer = buffer - 1; + bit_stream->bytes = available; + bit_stream->bits = 0; +} + +/*****************************************************************************/ +uint32_t vc_container_bits_invalidate( VC_CONTAINER_BITS_T *bit_stream ) +{ + bit_stream->buffer = NULL; + return 0; +} + +/*****************************************************************************/ +bool vc_container_bits_valid(VC_CONTAINER_BITS_T *bit_stream) +{ + return (bit_stream->buffer != NULL); +} + +/*****************************************************************************/ +void vc_container_bits_reset(VC_CONTAINER_BITS_T *bit_stream) +{ + bit_stream->bytes = 0; + bit_stream->bits = 0; +} + +/*****************************************************************************/ +const uint8_t *vc_container_bits_current_pointer(const VC_CONTAINER_BITS_T *bit_stream) +{ + const uint8_t *buffer = bit_stream->buffer; + + /* Only valid on byte boundaries, where buffer pointer has not been moved yet */ + vc_container_assert(!bit_stream->bits); + + return buffer ? (buffer + 1) : NULL; +} + +/*****************************************************************************/ +void vc_container_bits_copy_stream(VC_CONTAINER_BITS_T *dst, + const VC_CONTAINER_BITS_T *src) +{ + memcpy(dst, src, sizeof(VC_CONTAINER_BITS_T)); +} + +/*****************************************************************************/ +uint32_t vc_container_bits_available(const VC_CONTAINER_BITS_T *bit_stream) +{ + if (!bit_stream->buffer) + return 0; + return (bit_stream->bytes << 3) + bit_stream->bits; +} + +/*****************************************************************************/ +uint32_t vc_container_bits_bytes_available(const VC_CONTAINER_BITS_T *bit_stream) +{ + if (!bit_stream->buffer) + return 0; + + vc_container_assert(!bit_stream->bits); + + return vc_container_bits_available(bit_stream) >> 3; +} + +/*****************************************************************************/ +void vc_container_bits_skip(VC_CONTAINER_BITS_T *bit_stream, + uint32_t bits_to_skip) +{ + uint32_t have_bits; + uint32_t new_bytes; + + have_bits = vc_container_bits_available(bit_stream); + if (have_bits < bits_to_skip) + { + vc_container_bits_invalidate(bit_stream); + return; + } + + have_bits -= bits_to_skip; + new_bytes = have_bits >> 3; + bit_stream->bits = have_bits & 7; + bit_stream->buffer += (bit_stream->bytes - new_bytes); + bit_stream->bytes = new_bytes; +} + +/*****************************************************************************/ +void vc_container_bits_skip_bytes(VC_CONTAINER_BITS_T *bit_stream, + uint32_t bytes_to_skip) +{ + /* Only valid on byte boundaries */ + vc_container_assert(!bit_stream->bits); + + vc_container_bits_skip(bit_stream, bytes_to_skip << 3); +} + +/*****************************************************************************/ +void vc_container_bits_reduce_bytes(VC_CONTAINER_BITS_T *bit_stream, + uint32_t bytes_to_reduce) +{ + if (bit_stream->bytes >= bytes_to_reduce) + bit_stream->bytes -= bytes_to_reduce; + else + vc_container_bits_invalidate(bit_stream); +} + +/*****************************************************************************/ +void vc_container_bits_copy_bytes(VC_CONTAINER_BITS_T *bit_stream, + uint32_t bytes_to_copy, + uint8_t *dst) +{ + vc_container_assert(!bit_stream->bits); + + if (bit_stream->bytes < bytes_to_copy) + { + /* Not enough data */ + vc_container_bits_invalidate(bit_stream); + return; + } + + /* When the number of bits is zero, the next byte to take is at buffer + 1 */ + memcpy(dst, bit_stream->buffer + 1, bytes_to_copy); + bit_stream->buffer += bytes_to_copy; + bit_stream->bytes -= bytes_to_copy; +} + +/*****************************************************************************/ +uint32_t vc_container_bits_read_u32(VC_CONTAINER_BITS_T *bit_stream, + uint32_t value_bits) +{ + uint32_t value = 0; + uint32_t needed = value_bits; + uint32_t bits; + + vc_container_assert(value_bits <= 32); + + if (needed > vc_container_bits_available(bit_stream)) + return vc_container_bits_invalidate(bit_stream); + + bits = bit_stream->bits; + while (needed) + { + uint32_t take; + + if (!bits) + { + bit_stream->bytes--; + bit_stream->buffer++; + bits = 8; + } + + take = bits; + if (needed < take) take = needed; + + bits -= take; + needed -= take; + + value <<= take; + if (take == 8) + value |= *bit_stream->buffer; /* optimize whole byte case */ + else + value |= (*bit_stream->buffer >> bits) & ((1 << take) - 1); + } + + bit_stream->bits = bits; + return value; +} + +/*****************************************************************************/ +void vc_container_bits_skip_exp_golomb(VC_CONTAINER_BITS_T *bit_stream) +{ + vc_container_bits_skip(bit_stream, vc_container_bits_get_leading_zero_bits(bit_stream)); +} + +/*****************************************************************************/ +uint32_t vc_container_bits_read_u32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream) +{ + uint32_t leading_zero_bits; + uint32_t codeNum; + + leading_zero_bits = vc_container_bits_get_leading_zero_bits(bit_stream); + + /* Anything bigger than 32 bits is definitely overflow */ + if (leading_zero_bits > 32) + return vc_container_bits_invalidate(bit_stream); + + codeNum = vc_container_bits_read_u32(bit_stream, leading_zero_bits); + + if (leading_zero_bits == 32) + { + /* If codeNum is non-zero, it would need 33 bits, so is also overflow */ + if (codeNum) + return vc_container_bits_invalidate(bit_stream); + + return 0xFFFFFFFF; + } + + return codeNum + (1 << leading_zero_bits) - 1; +} + +/*****************************************************************************/ +int32_t vc_container_bits_read_s32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream) +{ + uint32_t uval; + + uval = vc_container_bits_read_u32_exp_golomb(bit_stream); + + /* The signed Exp-Golomb code 0xFFFFFFFF cannot be represented as a signed 32-bit + * integer, because it should be one larger than the largest positive value. */ + if (uval == 0xFFFFFFFF) + return vc_container_bits_invalidate(bit_stream); + + /* Definition of conversion is + * s = ((-1)^(u + 1)) * Ceil(u / 2) + * where '^' is power, but this should be equivalent */ + return ((int32_t)((uval & 1) << 1) - 1) * (int32_t)((uval >> 1) + (uval & 1)); +} + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT + +/*****************************************************************************/ +void vc_container_bits_log(VC_CONTAINER_T *p_ctx, + uint32_t indent, + const char *txt, + VC_CONTAINER_BITS_T *bit_stream, + VC_CONTAINER_BITS_LOG_OP_T op, + uint32_t length) +{ + const char *valid_str = vc_container_bits_valid_str(bit_stream); + const char *indent_str = vc_container_bits_indent_str(indent); + + switch (op) + { + case VC_CONTAINER_BITS_LOG_SKIP: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bits skipped%s", indent_str, txt, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_SKIP_BYTES: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes skipped%s", indent_str, txt, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_COPY_BYTES: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes copied%s", indent_str, txt, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_REDUCE_BYTES: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes reduced%s", indent_str, txt, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_EG_SKIP: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: Exp-Golomb value skipped%s", indent_str, txt, valid_str); + break; + default: + /* Unexpected operation. Check bit stream logging macros */ + vc_container_assert(0); + } +} + +/*****************************************************************************/ +uint32_t vc_container_bits_log_u32(VC_CONTAINER_T *p_ctx, + uint32_t indent, + const char *txt, + VC_CONTAINER_BITS_T *bit_stream, + VC_CONTAINER_BITS_LOG_OP_T op, + uint32_t length, + uint32_t value) +{ + const char *valid_str = vc_container_bits_valid_str(bit_stream); + const char *indent_str = vc_container_bits_indent_str(indent); + + switch (op) + { + case VC_CONTAINER_BITS_LOG_U8: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%02x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_U16: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%04x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_U32: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str); + break; + case VC_CONTAINER_BITS_LOG_EG_U32: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%u) unsigned Exp-Golomb%s", indent_str, txt, value, value, valid_str); + break; + default: + /* Unexpected operation. Check bit stream logging macros */ + vc_container_assert(0); + } + + return value; +} + +/*****************************************************************************/ +int32_t vc_container_bits_log_s32(VC_CONTAINER_T *p_ctx, + uint32_t indent, + const char *txt, + VC_CONTAINER_BITS_T *bit_stream, + VC_CONTAINER_BITS_LOG_OP_T op, + uint32_t length, + int32_t value) +{ + const char *valid_str = vc_container_bits_valid_str(bit_stream); + const char *indent_str = vc_container_bits_indent_str(indent); + + VC_CONTAINER_PARAM_UNUSED(length); + + switch (op) + { + case VC_CONTAINER_BITS_LOG_EG_S32: + vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%d) signed Exp-Golomb%s", indent_str, txt, value, value, valid_str); + break; + default: + /* Unexpected operation. Check bit stream logging macros */ + vc_container_assert(0); + } + + return value; +} + +#endif /* ENABLE_CONTAINERS_LOG_FORMAT */ diff --git a/containers/core/containers_bits.h b/containers/core/containers_bits.h new file mode 100755 index 0000000..10f1cd1 --- /dev/null +++ b/containers/core/containers_bits.h @@ -0,0 +1,344 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_CONTAINERS_BITS_H +#define VC_CONTAINERS_BITS_H + +#include "containers/containers.h" + +/** Bit stream structure + * Value are read from the buffer, taking bits from MSB to LSB in sequential + * bytes until the number of bit and the number of bytes runs out. */ +typedef struct vc_container_bits_tag +{ + const uint8_t *buffer; /**< Buffer from which to take bits */ + uint32_t bytes; /**< Number of bytes available from buffer */ + uint32_t bits; /**< Number of bits available at current pointer */ +} VC_CONTAINER_BITS_T; + +/** Initialise a bit stream object. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object to initialise. + * \param buffer Pointer to the start of the byte buffer. + * \param available Number of bytes in the bit stream. + */ +void vc_container_bits_init(VC_CONTAINER_BITS_T *bit_stream, const uint8_t *buffer, uint32_t available); + +/** Invalidates the bit stream. + * Also returns zero, because it allows callers that need to invalidate and + * immediately return zero to do so in a single statement. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return Zero, always. + */ +uint32_t vc_container_bits_invalidate( VC_CONTAINER_BITS_T *bit_stream ); + +/** Returns true if the bit stream is currently valid. + * The stream becomes invalid when a read or skip operation goe beyond the end + * of the stream. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return True if the stream is valid, false if it is invalid. + */ +bool vc_container_bits_valid(VC_CONTAINER_BITS_T *bit_stream); + +/** Reset a valid bit stream object to appear empty. + * Once a stream has become invalid, reset has no effect. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + */ +void vc_container_bits_reset(VC_CONTAINER_BITS_T *bit_stream); + +/** Return the current byte pointer for the bit stream. + * + * \pre bit_stream is not NULL. + * \pre The stream is on a byte boundary. + * + * \param bit_stream The bit stream object. + * \return The current byte pointer, or NULL if the stream is invalid. + */ +const uint8_t *vc_container_bits_current_pointer(const VC_CONTAINER_BITS_T *bit_stream); + +/** Copy one bit stream to another. + * If the source stream is invalid, the destination one will become so as well. + * + * \pre Neither bit stream is NULL. + * + * \param dst The destination bit stream object. + * \param src The source bit stream object. + */ +void vc_container_bits_copy_stream(VC_CONTAINER_BITS_T *dst, const VC_CONTAINER_BITS_T *src); + +/** Return the number of bits left to take from the stream. + * If the stream is invalid, zero is returned. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return The number of bits left to take. + */ +uint32_t vc_container_bits_available(const VC_CONTAINER_BITS_T *bit_stream); + +/** Return the number of bytes left to take from the stream. + * If the stream is invalid, zero is returned. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return The number of bytes left to take. + */ +uint32_t vc_container_bits_bytes_available(const VC_CONTAINER_BITS_T *bit_stream); + +/** Skip past a number of bits in the stream. + * If bits_to_skip is greater than the number of bits available in the stream, + * the stream becomes invalid. + * If the stream is already invalid, this has no effect. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \param bits_to_skip The number of bits to skip. + */ +void vc_container_bits_skip(VC_CONTAINER_BITS_T *bit_stream, uint32_t bits_to_skip); + +/** Skip past a number of bytes in the stream. + * If bytes_to_skip is greater than the number of bytes available in the stream, + * the stream becomes invalid. + * If the stream is already invalid, this has no effect. + * + * \pre bit_stream is not NULL. + * \pre The stream is on a byte boundary. + * + * \param bit_stream The bit stream object. + * \param bytes_to_skip The number of bytes to skip. + */ +void vc_container_bits_skip_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_skip); + +/** Reduce the length of the bit stream by a number of bytes. + * This reduces the number of bits/bytes available without changing the current + * position in the stream. If bytes_to_reduce is greater than the number of + * bytes available in the stream, the stream becomes invalid. + * If the stream is already invalid, this has no effect. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \param bytes_to_reduce The number of bytes by which to reduce the stream. + */ +void vc_container_bits_reduce_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_reduce); + +/** Copies a number of bytes from the stream to a byte buffer. + * If the stream is or becomes invalid, no data is copied. + * + * \pre bit_stream is not NULL. + * \pre The stream is on a byte boundary. + * + * \param bit_stream The bit stream object. + * \param bytes_to_copy The number of bytes to copy. + * \param dst The byte buffer destination. + */ +void vc_container_bits_copy_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_copy, uint8_t *dst); + +/** Returns the next value_bits from the stream. The last bit will be the least + * significant bit in the returned value. + * If value_bits is greater than the number of bits available in the stream, + * the stream becomes invalid. + * If the stream is invalid, or becomes invalid while reading the value, zero + * is returned. + * + * \pre bit_stream is not NULL. + * \pre value_bits is not larger than 32. + * + * \param bit_stream The bit stream object. + * \param value_bits The number of bits to retrieve. + * \return The value read from the stream, or zero if the stream is invalid. + */ +uint32_t vc_container_bits_read_u32(VC_CONTAINER_BITS_T *bit_stream, uint32_t value_bits); + +/** Skips the next Exp-Golomb value in the stream. + * See section 9.1 of ITU-T REC H.264 201003 for details. + * If there are not enough bits in the stream to complete an Exp-Golomb value, + * the stream becomes invalid. + * If the stream is already invalid, this has no effect. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + */ +void vc_container_bits_skip_exp_golomb(VC_CONTAINER_BITS_T *bit_stream); + +/** Returns the next unsigned Exp-Golomb value from the stream. + * See section 9.1 of ITU-T REC H.264 201003 for details. + * If there are not enough bits in the stream to complete an Exp-Golomb value, + * the stream becomes invalid. + * If the next unsigned Exp-Golomb value in the stream is larger than 32 bits, + * or the stream is or becomes invalid, zero is returned. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return The next unsigned value from the stream, or zero on error. + */ +uint32_t vc_container_bits_read_u32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream); + +/** Returns the next signed Exp-Golomb value from the stream. + * See section 9.1.1 of ITU-T REC H.264 201003 for details. + * If there are not enough bits in the stream to complete an Exp-Golomb value, + * the stream becomes invalid. + * If the next signed Exp-Golomb value in the stream is larger than 32 bits, + * or the stream is or becomes invalid, zero is returned. + * + * \pre bit_stream is not NULL. + * + * \param bit_stream The bit stream object. + * \return The next signed value from the stream, or zero on error. + */ +int32_t vc_container_bits_read_s32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream); + +/****************************************************************************** + * Macros reduce function name length and enable logging of some operations * + ******************************************************************************/ +#define BITS_INIT(ctx, bits, buffer, available) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_init(bits, buffer, available)) +#define BITS_INVALIDATE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_invalidate(bits)) +#define BITS_VALID(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_valid(bits)) +#define BITS_RESET(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_reset(bits)) +#define BITS_AVAILABLE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_available(bits)) +#define BITS_BYTES_AVAILABLE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_bytes_available(bits)) +#define BITS_CURRENT_POINTER(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_current_pointer(bits)) +#define BITS_COPY_STREAM(ctx, dst, src) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_copy_stream(dst, src)) + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT + +typedef enum { + VC_CONTAINER_BITS_LOG_SKIP, + VC_CONTAINER_BITS_LOG_SKIP_BYTES, + VC_CONTAINER_BITS_LOG_U8, + VC_CONTAINER_BITS_LOG_U16, + VC_CONTAINER_BITS_LOG_U32, + VC_CONTAINER_BITS_LOG_COPY_BYTES, + VC_CONTAINER_BITS_LOG_REDUCE_BYTES, + VC_CONTAINER_BITS_LOG_EG_SKIP, + VC_CONTAINER_BITS_LOG_EG_U32, + VC_CONTAINER_BITS_LOG_EG_S32, +} VC_CONTAINER_BITS_LOG_OP_T; + +/** Logs an operation with void return. + * + * \pre None of p_ctx, txt or bit_stream are NULL. + * + * \param p_ctx Container context. + * \param indent Indent level. + * \param txt Description of what is being read. + * \param bit_stream The bit stream object. + * \param op The operation just performed. + * \param length The length of the operation. + */ +void vc_container_bits_log(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length); + +/** Logs an operation with unsigned 32-bit integer return. + * + * \pre None of p_ctx, txt or bit_stream are NULL. + * + * \param p_ctx Container context. + * \param indent Indent level. + * \param txt Description of what is being read. + * \param bit_stream The bit stream object. + * \param op The operation just performed. + * \param length The length of the operation. + * \param value The value returned by the operation. + * \return The unsigned 32-bit integer value passed in. + */ +uint32_t vc_container_bits_log_u32(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length, uint32_t value); + +/** Logs an operation with signed 32-bit integer return. + * + * \pre None of p_ctx, txt or bit_stream are NULL. + * + * \param p_ctx Container context. + * \param indent Indent level. + * \param txt Description of what is being read. + * \param bit_stream The bit stream object. + * \param op The operation just performed. + * \param length The length of the operation. + * \param value The value returned by the operation. + * \return The signed 32-bit integer value passed in. + */ +int32_t vc_container_bits_log_s32(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length, int32_t value); + +#ifndef BITS_LOG_INDENT +# ifndef CONTAINER_HELPER_LOG_INDENT +# define BITS_LOG_INDENT(ctx) 0 +# else +# define BITS_LOG_INDENT(ctx) ((ctx)->priv->io->module ? CONTAINER_HELPER_LOG_INDENT(a) : 0) +# endif +#endif + +#define BITS_SKIP(ctx, bits, length, txt) (vc_container_bits_skip(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_SKIP, length)) +#define BITS_SKIP_BYTES(ctx, bits, length, txt) (vc_container_bits_skip_bytes(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_SKIP_BYTES, length)) + +#define BITS_READ_U8(ctx, bits, length, txt) (uint8_t)vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U8, length, vc_container_bits_read_u32(bits, length)) +#define BITS_READ_U16(ctx, bits, length, txt) (uint16_t)vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U16, length, vc_container_bits_read_u32(bits, length)) +#define BITS_READ_U32(ctx, bits, length, txt) vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U32, length, vc_container_bits_read_u32(bits, length)) + +#define BITS_COPY_BYTES(ctx, bits, length, dst, txt) (vc_container_bits_copy_bytes(bits, length, dst), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_COPY_BYTES, length)) + +#define BITS_REDUCE_BYTES(ctx, bits, length, txt) (vc_container_bits_reduce_bytes(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_REDUCE_BYTES, length)) + +#define BITS_SKIP_EXP(ctx, bits, txt) (vc_container_bits_skip_exp_golomb(bits), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_SKIP, 0)) + +#define BITS_READ_S32_EXP(ctx, bits, txt) vc_container_bits_log_s32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_S32, 0, vc_container_bits_read_s32_exp_golomb(bits)) +#define BITS_READ_U32_EXP(ctx, bits, txt) vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_U32, 0, vc_container_bits_read_u32_exp_golomb(bits)) + +#else /* ENABLE_CONTAINERS_LOG_FORMAT */ + +#define BITS_SKIP(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip(bits, length)) +#define BITS_SKIP_BYTES(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip_bytes(bits, length)) + +#define BITS_READ_U8(ctx, bits, length, txt) (uint8_t)(VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length)) +#define BITS_READ_U16(ctx, bits, length, txt) (uint16_t)(VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length)) +#define BITS_READ_U32(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length)) + +#define BITS_COPY_BYTES(ctx, bits, length, dst, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_copy_bytes(bits, length, dst)) + +#define BITS_REDUCE_BYTES(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_reduce_bytes(bits, length)) + +#define BITS_SKIP_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip_exp_golomb(bits)) + +#define BITS_READ_S32_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_s32_exp_golomb(bits)) +#define BITS_READ_U32_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32_exp_golomb(bits)) + +#endif /* ENABLE_CONTAINERS_LOG_FORMAT */ + +#endif /* VC_CONTAINERS_BITS_H */ diff --git a/containers/core/containers_bytestream.h b/containers/core/containers_bytestream.h new file mode 100755 index 0000000..95b3a61 --- /dev/null +++ b/containers/core/containers_bytestream.h @@ -0,0 +1,389 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_BYTESTREAM_H +#define VC_CONTAINERS_BYTESTREAM_H + +/** \file + * Utility functions to provide a byte stream out of a list of container packets + */ + +typedef struct VC_CONTAINER_BYTESTREAM_T +{ + VC_CONTAINER_PACKET_T *first; /**< first packet in the chain */ + VC_CONTAINER_PACKET_T **last; /**< last packet in the chain */ + + VC_CONTAINER_PACKET_T *current; /**< packet containing the current read pointer */ + size_t current_offset; /**< position of current packet (in bytes) */ + size_t offset; /**< position within the current packet */ + + size_t bytes; /**< Number of bytes available in the bytestream */ + +} VC_CONTAINER_BYTESTREAM_T; + +/*****************************************************************************/ +STATIC_INLINE void bytestream_init( VC_CONTAINER_BYTESTREAM_T *stream ) +{ + stream->first = stream->current = NULL; + stream->last = &stream->first; + stream->offset = stream->current_offset = stream->bytes = 0; +} + +STATIC_INLINE void bytestream_push( VC_CONTAINER_BYTESTREAM_T *stream, + VC_CONTAINER_PACKET_T *packet ) +{ + *stream->last = packet; + stream->last = &packet->next; + packet->next = NULL; + if( !stream->current ) stream->current = packet; + stream->bytes += packet->size; +} + +STATIC_INLINE VC_CONTAINER_PACKET_T *bytestream_pop( VC_CONTAINER_BYTESTREAM_T *stream ) +{ + VC_CONTAINER_PACKET_T *packet = stream->first; + + if( stream->current == packet ) + return NULL; + vc_container_assert(packet); + + stream->bytes -= packet->size; + stream->current_offset -= packet->size; + stream->first = packet->next; + if( !stream->first ) + stream->last = &stream->first; + return packet; +} + +STATIC_INLINE VC_CONTAINER_PACKET_T *bytestream_get_packet( VC_CONTAINER_BYTESTREAM_T *stream, size_t *offset ) +{ + VC_CONTAINER_PACKET_T *packet = stream->current; + size_t off = stream->offset; + + while(packet && packet->size == off) + { + packet = packet->next; + off = 0; + } + + if (offset) + *offset = off; + + return packet; +} + +STATIC_INLINE bool bytestream_skip_packet( VC_CONTAINER_BYTESTREAM_T *stream ) +{ + VC_CONTAINER_PACKET_T *packet = stream->current; + + if( packet ) + { + stream->current = packet->next; + stream->current_offset += (packet->size - stream->offset); + stream->offset = 0; + } + + return !!packet; +} + +STATIC_INLINE void bytestream_get_timestamps( VC_CONTAINER_BYTESTREAM_T *stream, int64_t *pts, int64_t *dts, bool b_same ) +{ + VC_CONTAINER_PACKET_T *packet = bytestream_get_packet( stream, 0 ); + + if(packet) + { + if(b_same && packet->pts == VC_CONTAINER_TIME_UNKNOWN) packet->pts = packet->dts; + if(pts) *pts = packet->pts; + if(dts) *dts = packet->dts; + + packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN; + return; + } + + if(pts) *pts = VC_CONTAINER_TIME_UNKNOWN; + if(dts) *dts = VC_CONTAINER_TIME_UNKNOWN; +} + +STATIC_INLINE void bytestream_get_timestamps_and_offset( VC_CONTAINER_BYTESTREAM_T *stream, + int64_t *pts, int64_t *dts, size_t *offset, bool b_same ) +{ + VC_CONTAINER_PACKET_T *packet = bytestream_get_packet( stream, offset ); + + if(packet) + { + if(b_same && packet->pts == VC_CONTAINER_TIME_UNKNOWN) packet->pts = packet->dts; + if(pts) *pts = packet->pts; + if(dts) *dts = packet->dts; + + packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN; + return; + } + + if(pts) *pts = VC_CONTAINER_TIME_UNKNOWN; + if(dts) *dts = VC_CONTAINER_TIME_UNKNOWN; +} + +STATIC_INLINE size_t bytestream_size( VC_CONTAINER_BYTESTREAM_T *stream ) +{ + return stream->bytes - stream->current_offset - stream->offset; +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_skip( VC_CONTAINER_BYTESTREAM_T *stream, size_t size ) +{ + VC_CONTAINER_PACKET_T *packet; + size_t offset, bytes = 0, skip; + + if( !size ) + return VC_CONTAINER_SUCCESS; /* Nothing to do */ + if( stream->bytes - stream->current_offset - stream->offset < size ) + return VC_CONTAINER_ERROR_EOS; /* Not enough data */ + + for( packet = stream->current, offset = stream->offset; ; + packet = packet->next, offset = 0 ) + { + if( packet->size - offset >= size) + break; + + skip = packet->size - offset; + bytes += skip; + size -= skip; + } + + stream->current = packet; + stream->current_offset += stream->offset - offset + bytes; + stream->offset = offset + size; + return VC_CONTAINER_SUCCESS; +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_get( VC_CONTAINER_BYTESTREAM_T *stream, uint8_t *data, size_t size ) +{ + VC_CONTAINER_PACKET_T *packet; + size_t offset, bytes = 0, copy; + + if( !size ) + return VC_CONTAINER_SUCCESS; /* Nothing to do */ + if( stream->bytes - stream->current_offset - stream->offset < size ) + return VC_CONTAINER_ERROR_EOS; /* Not enough data */ + + for( packet = stream->current, offset = stream->offset; ; + packet = packet->next, offset = 0 ) + { + if( packet->size - offset >= size) + break; + + copy = packet->size - offset; + memcpy( data, packet->data + offset, copy ); + bytes += copy; + data += copy; + size -= copy; + } + + memcpy( data, packet->data + offset, size ); + stream->current = packet; + stream->current_offset += stream->offset - offset + bytes; + stream->offset = offset + size; + return VC_CONTAINER_SUCCESS; +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_peek( VC_CONTAINER_BYTESTREAM_T *stream, uint8_t *data, size_t size ) +{ + VC_CONTAINER_PACKET_T *packet; + size_t offset, copy; + + if( !size ) + return VC_CONTAINER_SUCCESS; /* Nothing to do */ + if( stream->bytes - stream->current_offset - stream->offset < size ) + return VC_CONTAINER_ERROR_EOS; /* Not enough data */ + + for( packet = stream->current, offset = stream->offset; ; + packet = packet->next, offset = 0 ) + { + if( packet->size - offset >= size) + break; + + copy = packet->size - offset; + memcpy( data, packet->data + offset, copy ); + data += copy; + size -= copy; + } + + memcpy( data, packet->data + offset, size ); + return VC_CONTAINER_SUCCESS; +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_peek_at( VC_CONTAINER_BYTESTREAM_T *stream, + size_t peek_offset, uint8_t *data, size_t size ) +{ + VC_CONTAINER_PACKET_T *packet; + size_t copy; + + if( !size ) + return VC_CONTAINER_SUCCESS; /* Nothing to do */ + if( stream->bytes - stream->current_offset - stream->offset < peek_offset + size ) + return VC_CONTAINER_ERROR_EOS; /* Not enough data */ + + peek_offset += stream->offset; + + /* Find the right place */ + for( packet = stream->current; ; packet = packet->next ) + { + if( packet->size > peek_offset ) + break; + + peek_offset -= packet->size; + } + + /* Copy the data */ + for( ; ; packet = packet->next, peek_offset = 0 ) + { + if( packet->size - peek_offset >= size) + break; + + copy = packet->size - peek_offset; + memcpy( data, packet->data + peek_offset, copy ); + data += copy; + size -= copy; + } + + memcpy( data, packet->data + peek_offset, size ); + return VC_CONTAINER_SUCCESS; +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_skip_byte( VC_CONTAINER_BYTESTREAM_T *stream ) +{ + VC_CONTAINER_PACKET_T *packet = stream->current; + + if( !packet ) + return VC_CONTAINER_ERROR_EOS; + + /* Fast path first */ + if( packet->size - stream->offset ) + { + stream->offset++; + return VC_CONTAINER_SUCCESS; + } + + return bytestream_skip( stream, 1 ); +} + +STATIC_INLINE VC_CONTAINER_STATUS_T packet_peek_byte( VC_CONTAINER_BYTESTREAM_T *stream, + uint8_t *data ) +{ + VC_CONTAINER_PACKET_T *packet = stream->current; + + if( !packet ) + return VC_CONTAINER_ERROR_EOS; + + /* Fast path first */ + if( packet->size - stream->offset ) + { + *data = packet->data[stream->offset]; + return VC_CONTAINER_SUCCESS; + } + + return bytestream_peek( stream, data, 1 ); +} + +STATIC_INLINE VC_CONTAINER_STATUS_T packet_get_byte( VC_CONTAINER_BYTESTREAM_T *stream, + uint8_t *data ) +{ + VC_CONTAINER_PACKET_T *packet = stream->current; + + if( !packet ) + return VC_CONTAINER_ERROR_EOS; + + /* Fast path first */ + if( packet->size - stream->offset ) + { + *data = packet->data[stream->offset]; + stream->offset++; + return VC_CONTAINER_SUCCESS; + } + + return bytestream_get( stream, data, 1 ); +} + +STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_find_startcode( VC_CONTAINER_BYTESTREAM_T *stream, + size_t *search_offset, const uint8_t *startcode, unsigned int length ) +{ + VC_CONTAINER_PACKET_T *packet, *backup_packet = NULL; + size_t position, start_offset = position = *search_offset; + size_t offset, backup_offset = 0; + unsigned int match = 0; + + if( stream->bytes - stream->current_offset - stream->offset < start_offset + length ) + return VC_CONTAINER_ERROR_EOS; /* Not enough data */ + + /* Find the right place */ + for( packet = stream->current, offset = stream->offset; + packet != NULL; packet = packet->next, offset = 0 ) + { + if( packet->size - offset > start_offset) + break; + + start_offset -= (packet->size - offset); + } + + /* Start the search for the start code. + * To make things simple we try to find a match one byte at a time. */ + for( offset += start_offset; + packet != NULL; packet = packet->next, offset = 0 ) + { + for( ; offset < packet->size; offset++ ) + { + if( packet->data[offset] != startcode[match] ) + { + if ( match ) /* False positive */ + { + packet = backup_packet; + offset = backup_offset; + match = 0; + } + position++; + continue; + } + + /* We've got a match */ + if( !match++ ) + { + backup_packet = packet; + backup_offset = offset; + } + + if( match == length ) + { + /* We have the full start code it */ + *search_offset = position; + return VC_CONTAINER_SUCCESS; + } + } + } + + *search_offset = position; + return VC_CONTAINER_ERROR_EOS; /* No luck in finding the start code */ +} + +#endif /* VC_CONTAINERS_BYTESTREAM_H */ diff --git a/containers/core/containers_codecs.c b/containers/core/containers_codecs.c new file mode 100755 index 0000000..16e1593 --- /dev/null +++ b/containers/core/containers_codecs.c @@ -0,0 +1,209 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/containers_codecs.h" +#include "containers/core/containers_utils.h" + +/*****************************************************************************/ +static struct { + VC_CONTAINER_FOURCC_T codec; + uint16_t id; +} codec_to_wf_table[] = +{ + {VC_CONTAINER_CODEC_PCM_SIGNED_LE, WAVE_FORMAT_PCM}, + {VC_CONTAINER_CODEC_ALAW, WAVE_FORMAT_ALAW}, + {VC_CONTAINER_CODEC_MULAW, WAVE_FORMAT_MULAW}, + {VC_CONTAINER_CODEC_ADPCM_MS, WAVE_FORMAT_ADPCM}, + {VC_CONTAINER_CODEC_MPGA, WAVE_FORMAT_MPEG}, + {VC_CONTAINER_CODEC_MPGA, WAVE_FORMAT_MPEGLAYER3}, + {VC_CONTAINER_CODEC_WMA1, WAVE_FORMAT_WMAUDIO1}, + {VC_CONTAINER_CODEC_WMA2, WAVE_FORMAT_WMAUDIO2}, + {VC_CONTAINER_CODEC_WMAP, WAVE_FORMAT_WMAUDIOPRO}, + {VC_CONTAINER_CODEC_WMAL, WAVE_FORMAT_WMAUDIO_LOSSLESS}, + {VC_CONTAINER_CODEC_WMAV, WAVE_FORMAT_WMAUDIO_VOICE}, + {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_DVM}, + {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_DOLBY_AC3_SPDIF}, /**< AC-3 padded for S/PDIF */ + {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_RAW_SPORT}, /**< AC-3 padded for S/PDIF */ + {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_ESST_AC3}, /**< AC-3 padded for S/PDIF */ + {VC_CONTAINER_CODEC_EAC3, WAVE_FORMAT_DVM}, + {VC_CONTAINER_CODEC_DTS, WAVE_FORMAT_DTS}, +#if 0 + {CODEC_G726, WAVE_FORMAT_G726_ADPCM}, + {CODEC_G726, WAVE_FORMAT_DF_G726}, + {CODEC_G726, WAVE_FORMAT_G726ADPCM}, + {CODEC_G726, WAVE_FORMAT_PANASONIC_G726}, +#endif + {VC_CONTAINER_CODEC_MP4A, WAVE_FORMAT_AAC}, + {VC_CONTAINER_CODEC_MP4A, WAVE_FORMAT_MP4A}, + {VC_CONTAINER_CODEC_ATRAC3, WAVE_FORMAT_SONY_SCX}, + {VC_CONTAINER_CODEC_UNKNOWN, WAVE_FORMAT_UNKNOWN} +}; + +VC_CONTAINER_FOURCC_T waveformat_to_codec(uint16_t waveformat_id) +{ + unsigned int i; + for(i = 0; codec_to_wf_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_wf_table[i].id == waveformat_id) break; + return codec_to_wf_table[i].codec; +} + +uint16_t codec_to_waveformat(VC_CONTAINER_FOURCC_T codec) +{ + unsigned int i; + for(i = 0; codec_to_wf_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_wf_table[i].codec == codec) break; + return codec_to_wf_table[i].id; +} + +static struct { + VC_CONTAINER_FOURCC_T codec; + uint32_t fourcc; +} codec_to_vfw_table[] = +{ +#if defined(ENABLE_CONTAINERS_STANDALONE) || !defined(NDEBUG) + /* We are legally required to not play DivX in RELEASE mode. See Jira SW-3138 */ + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('D','I','V','3')}, + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('d','i','v','3')}, + {VC_CONTAINER_CODEC_DIV4, VC_FOURCC('D','I','V','4')}, + {VC_CONTAINER_CODEC_DIV4, VC_FOURCC('d','i','v','4')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('D','X','5','0')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('D','I','V','X')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('d','i','v','x')}, +#endif /* ENABLE_CONTAINERS_STANDALONE || !NDEBUG */ + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','V')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','v')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','S')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','s')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','4','S','2')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','4','s','2')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('F','M','P','4')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('X','V','I','D')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('x','v','i','d')}, + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('M','P','4','3')}, + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('m','p','4','3')}, + {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('m','p','g','1')}, + {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('M','P','G','1')}, + {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('m','p','g','2')}, + {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('M','P','G','2')}, + {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('M','J','P','G')}, + {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('m','j','p','g')}, + {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('W','M','V','1')}, + {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('w','m','v','1')}, + {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('W','M','V','2')}, + {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('w','m','v','2')}, + {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('W','M','V','3')}, + {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('w','m','v','3')}, + {VC_CONTAINER_CODEC_WVC1, VC_FOURCC('W','V','C','1')}, + {VC_CONTAINER_CODEC_WVC1, VC_FOURCC('w','v','c','1')}, + {VC_CONTAINER_CODEC_WMVA, VC_FOURCC('w','m','v','a')}, + {VC_CONTAINER_CODEC_WMVA, VC_FOURCC('W','M','V','A')}, + {VC_CONTAINER_CODEC_VP6, VC_FOURCC('V','P','6','F')}, + {VC_CONTAINER_CODEC_VP6, VC_FOURCC('v','p','6','f')}, + {VC_CONTAINER_CODEC_VP7, VC_FOURCC('V','P','7','0')}, + {VC_CONTAINER_CODEC_VP7, VC_FOURCC('v','p','7','0')}, + {VC_CONTAINER_CODEC_H263, VC_FOURCC('H','2','6','3')}, + {VC_CONTAINER_CODEC_H263, VC_FOURCC('h','2','6','3')}, + {VC_CONTAINER_CODEC_H264, VC_FOURCC('H','2','6','4')}, + {VC_CONTAINER_CODEC_H264, VC_FOURCC('h','2','6','4')}, + {VC_CONTAINER_CODEC_H264, VC_FOURCC('A','V','C','1')}, + {VC_CONTAINER_CODEC_H264, VC_FOURCC('a','v','c','1')}, + {VC_CONTAINER_CODEC_SPARK, VC_FOURCC('F','L','V','1')}, + {VC_CONTAINER_CODEC_SPARK, VC_FOURCC('f','l','v','1')}, + {VC_CONTAINER_CODEC_UNKNOWN, 0} +}; + +VC_CONTAINER_FOURCC_T vfw_fourcc_to_codec(uint32_t fourcc) +{ + unsigned int i; + for(i = 0; codec_to_vfw_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_vfw_table[i].fourcc == fourcc) break; + + if(codec_to_vfw_table[i].codec == VC_CONTAINER_CODEC_UNKNOWN) + return fourcc; + + return codec_to_vfw_table[i].codec; +} + +uint32_t codec_to_vfw_fourcc(VC_CONTAINER_FOURCC_T codec) +{ + unsigned int i; + for(i = 0; codec_to_vfw_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_vfw_table[i].codec == codec) break; + + return codec_to_vfw_table[i].fourcc; +} + +static struct { + VC_CONTAINER_FOURCC_T codec; + uint32_t fourcc; +} codec_to_fourcc_table[] = +{ + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','S')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','4','S','2')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','s')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','4','s','2')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','V')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','v')}, + {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('F','M','P','4')}, + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('M','P','4','3')}, + {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('m','p','4','3')}, + {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('W','M','V','1')}, + {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('w','m','v','1')}, + {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('W','M','V','2')}, + {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('w','m','v','2')}, + {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('W','M','V','3')}, + {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('w','m','v','3')}, + {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('m','p','g','1')}, + {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('M','P','G','1')}, + {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('m','p','g','2')}, + {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('M','P','G','2')}, + {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('M','J','P','G')}, + {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('m','j','p','g')}, + {VC_CONTAINER_CODEC_UNKNOWN, 0} +}; + +VC_CONTAINER_FOURCC_T fourcc_to_codec(uint32_t fourcc) +{ + unsigned int i; + for(i = 0; codec_to_fourcc_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_fourcc_table[i].fourcc == fourcc) break; + + return codec_to_fourcc_table[i].codec; +} + +uint32_t codec_to_fourcc(VC_CONTAINER_FOURCC_T codec) +{ + unsigned int i; + for(i = 0; codec_to_fourcc_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++) + if(codec_to_fourcc_table[i].codec == codec) break; + + return codec_to_fourcc_table[i].fourcc; +} diff --git a/containers/core/containers_common.h b/containers/core/containers_common.h new file mode 100755 index 0000000..eb18671 --- /dev/null +++ b/containers/core/containers_common.h @@ -0,0 +1,73 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_COMMON_H +#define VC_CONTAINERS_COMMON_H + +/** \file containers_common.h + * Common definitions for containers infrastructure + */ + +#ifndef ENABLE_CONTAINERS_STANDALONE +# include "vcos.h" +# define vc_container_assert(a) vcos_assert(a) +#else +# include "assert.h" +# define vc_container_assert(a) assert(a) +#endif /* ENABLE_CONTAINERS_STANDALONE */ + +#ifndef countof +# define countof(a) (sizeof(a) / sizeof(a[0])) +#endif + +#ifndef MIN +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifdef _MSC_VER +# define strcasecmp stricmp +# define strncasecmp strnicmp +#endif + +#define STATIC_INLINE static __inline +#define VC_CONTAINER_PARAM_UNUSED(a) (void)(a) + +#if defined(__HIGHC__) && !defined(strcasecmp) +# define strcasecmp(a,b) _stricmp(a,b) +#endif + +#if defined(__GNUC__) && (__GNUC__ > 2) +# define VC_CONTAINER_CONSTRUCTOR(func) void __attribute__((constructor,used)) func(void) +# define VC_CONTAINER_DESTRUCTOR(func) void __attribute__((destructor,used)) func(void) +#else +# define VC_CONTAINER_CONSTRUCTOR(func) void func(void) +# define VC_CONTAINER_DESTRUCTOR(func) void func(void) +#endif + +#endif /* VC_CONTAINERS_COMMON_H */ diff --git a/containers/core/containers_filters.c b/containers/core/containers_filters.c new file mode 100755 index 0000000..a5bc0b7 --- /dev/null +++ b/containers/core/containers_filters.c @@ -0,0 +1,222 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_private.h" +#include "containers/core/containers_filters.h" + +#if !defined(ENABLE_CONTAINERS_STANDALONE) + #include "vcos_dlfcn.h" + #define DL_SUFFIX VCOS_SO_EXT + #ifndef DL_PATH_PREFIX + #define DL_PATH_PREFIX "" + #endif +#endif + +typedef struct VC_CONTAINER_FILTER_PRIVATE_T +{ + /** Pointer to the container filter code and symbols */ + void *handle; +} VC_CONTAINER_FILTER_PRIVATE_T; + +typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_FILTER_OPEN_FUNC_T)(VC_CONTAINER_FILTER_T*, VC_CONTAINER_FOURCC_T); + +static VC_CONTAINER_FILTER_OPEN_FUNC_T load_library(void **handle, VC_CONTAINER_FOURCC_T filter, const char *name); +static void unload_library(void *handle); + +static struct { + VC_CONTAINER_FOURCC_T filter; + const char *name; +} filter_to_name_table[] = +{ + {VC_FOURCC('d','r','m',' '), "divx"}, + {VC_FOURCC('d','r','m',' '), "hdcp2"}, + {0, NULL} +}; + +static VC_CONTAINER_STATUS_T vc_container_filter_load(VC_CONTAINER_FILTER_T *p_ctx, + VC_CONTAINER_FOURCC_T filter, + VC_CONTAINER_FOURCC_T type) +{ + void *handle = NULL; + VC_CONTAINER_FILTER_OPEN_FUNC_T func; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + unsigned int i; + + for(i = 0; filter_to_name_table[i].filter; ++i) + { + if (filter_to_name_table[i].filter == filter) + { + if ((func = load_library(&handle, filter, filter_to_name_table[i].name)) != NULL) + { + status = (*func)(p_ctx, type); + if(status == VC_CONTAINER_SUCCESS) break; + unload_library(handle); + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) break; + } + } + } + + p_ctx->priv->handle = handle; + return status; +} + +static void vc_container_filter_unload(VC_CONTAINER_FILTER_T *p_ctx) +{ + unload_library(p_ctx->priv->handle); + p_ctx->priv->handle = NULL; +} + +/*****************************************************************************/ +VC_CONTAINER_FILTER_T *vc_container_filter_open(VC_CONTAINER_FOURCC_T filter, + VC_CONTAINER_FOURCC_T type, + VC_CONTAINER_T *p_container, + VC_CONTAINER_STATUS_T *p_status ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; + VC_CONTAINER_FILTER_T *p_ctx = 0; + VC_CONTAINER_FILTER_PRIVATE_T *priv = 0; + + /* Allocate our context before trying out the different filter modules */ + p_ctx = malloc(sizeof(*p_ctx) + sizeof(*priv)); + if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*priv)); + p_ctx->priv = priv = (VC_CONTAINER_FILTER_PRIVATE_T *)&p_ctx[1]; + p_ctx->container = p_container; + + status = vc_container_filter_load(p_ctx, filter, type); + if(status != VC_CONTAINER_SUCCESS) goto error; + + end: + if(p_status) *p_status = status; + return p_ctx; + + error: + if(p_ctx) free(p_ctx); + p_ctx = 0; + goto end; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_filter_close( VC_CONTAINER_FILTER_T *p_ctx ) +{ + if (p_ctx) + { + if(p_ctx->pf_close) p_ctx->pf_close(p_ctx); + if(p_ctx->priv && p_ctx->priv->handle) vc_container_filter_unload(p_ctx); + free(p_ctx); + } + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_filter_process( VC_CONTAINER_FILTER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet ) +{ + VC_CONTAINER_STATUS_T status; + status = p_ctx->pf_process(p_ctx, p_packet); + return status; +} + +VC_CONTAINER_STATUS_T vc_container_filter_control(VC_CONTAINER_FILTER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, ... ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + va_list args; + + va_start( args, operation ); + if(p_ctx->pf_control) + status = p_ctx->pf_control(p_ctx, operation, args); + va_end( args ); + + return status; +} + +static VC_CONTAINER_FILTER_OPEN_FUNC_T load_library(void **handle, VC_CONTAINER_FOURCC_T filter, const char *name) +{ + VC_CONTAINER_FILTER_OPEN_FUNC_T func = NULL; +#ifdef ENABLE_CONTAINERS_STANDALONE + VC_CONTAINER_PARAM_UNUSED(handle); + VC_CONTAINER_PARAM_UNUSED(filter); + VC_CONTAINER_PARAM_UNUSED(name); +#else + char *dl_name, *entrypt_name; + const char *entrypt_name_short = "filter_open"; + char filter_[6], *ptr; + void *dl_handle; + int dl_name_len; + int entrypt_name_len; + + snprintf(filter_, sizeof(filter_), "%4.4s", (const char*)&filter); + ptr = strchr(filter_, '\0'); + while (ptr > filter_ && isspace(*--ptr)) *ptr = '\0'; + strncat(filter_, "_", 1); + + dl_name_len = strlen(DL_PATH_PREFIX) + strlen(filter_) + strlen(name) + strlen(DL_SUFFIX) + 1; + dl_name = malloc(dl_name_len); + if (!dl_name) return NULL; + + entrypt_name_len = strlen(name) + 1 + strlen(filter_) + strlen(entrypt_name_short) + 1; + entrypt_name = malloc(entrypt_name_len); + if (!entrypt_name) + { + free(dl_name); + return NULL; + } + + snprintf(dl_name, dl_name_len, "%s%s%s%s", DL_PATH_PREFIX, filter_, name, DL_SUFFIX); + snprintf(entrypt_name, entrypt_name_len, "%s_%s%s", name, filter_, entrypt_name_short); + + if ((dl_handle = vcos_dlopen(dl_name, VCOS_DL_NOW)) != NULL) + { + /* Try generic entrypoint name before the mangled, full name */ + func = (VC_CONTAINER_FILTER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name_short); + if (!func) func = (VC_CONTAINER_FILTER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name); + /* Only return handle if symbol found */ + if (func) + *handle = dl_handle; + else + vcos_dlclose(dl_handle); + } + + free(dl_name); + free(entrypt_name); +#endif + return func; +} + +static void unload_library(void *handle) +{ +#ifdef ENABLE_CONTAINERS_STANDALONE + VC_CONTAINER_PARAM_UNUSED(handle); +#else + vcos_dlclose(handle); +#endif +} diff --git a/containers/core/containers_filters.h b/containers/core/containers_filters.h new file mode 100755 index 0000000..9026274 --- /dev/null +++ b/containers/core/containers_filters.h @@ -0,0 +1,110 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_FILTERS_H +#define VC_CONTAINERS_FILTERS_H + +/** \file containers_filters.h + * Interface definition for the filter abstraction used by the container + * common layer */ +#include + +#include "containers/containers.h" + +/** \defgroup VcContainerFilterApi Container Filter API */ +/* @{ */ + +/** Container Filter Context. + * This structure defines the context for a container filter instance */ +typedef struct VC_CONTAINER_FILTER_T +{ + /** Pointer to container instance */ + struct VC_CONTAINER_T *container; + /** Pointer to information private to the container filter instance */ + struct VC_CONTAINER_FILTER_PRIVATE_T *priv; + /** Pointer to information private to the container filter module */ + struct VC_CONTAINER_FILTER_MODULE_T *module; + + /** \note the following list of function pointers should not be used directly. + * They defines the interface for implementing container filter modules and are + * filled in by the container filter modules themselves. */ + + /** \private + * Function pointer to close and free all resources allocated by a + * container filter module */ + VC_CONTAINER_STATUS_T (*pf_close)(struct VC_CONTAINER_FILTER_T *filter); + + /** \private + * Function pointer to filter a data packet using a container filter module */ + VC_CONTAINER_STATUS_T (*pf_process)(struct VC_CONTAINER_FILTER_T *filter, VC_CONTAINER_PACKET_T *p_packet); + + /** \private + * Function pointer to control container filter module */ + VC_CONTAINER_STATUS_T (*pf_control)( struct VC_CONTAINER_FILTER_T *filter, VC_CONTAINER_CONTROL_T operation, va_list args ); + +} VC_CONTAINER_FILTER_T; + +/** Opens a container filter using a four character code describing the filter. + * This will create an instance of the container filter. + * + * \param filter Four Character Code describing the filter + * \param type Four Character Code describing the subtype - indicated whether filter is encrypt or decrypt + * \param container Pointer to the container instance + * \param status Returns the status of the operation + * \return If successful, this returns a pointer to the new instance + * of the container filter. Returns NULL on failure. + */ +VC_CONTAINER_FILTER_T *vc_container_filter_open(VC_CONTAINER_FOURCC_T filter, + VC_CONTAINER_FOURCC_T type, + VC_CONTAINER_T *container, + VC_CONTAINER_STATUS_T *status ); + +/** Closes an instance of a container filter. + * \param context Pointer to the VC_CONTAINER_FILTER_T context of the instance to close + * \return VC_CONTAINER_SUCCESS on success. + */ +VC_CONTAINER_STATUS_T vc_container_filter_close( VC_CONTAINER_FILTER_T *context ); + +/** Filter a data packet using a container filter module. + * \param context Pointer to the VC_CONTAINER_FILTER_T instance to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure to process + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_filter_process(VC_CONTAINER_FILTER_T *context, VC_CONTAINER_PACKET_T *p_packet); + +/** Extensible control function for container filter modules. +* This function takes a variable number of arguments which will depend on the specific operation. +* +* \param context Pointer to the VC_CONTAINER_FILTER_T instance to use +* \param operation The requested operation +* \param args Arguments for the operation +* \return the status of the operation +*/ +VC_CONTAINER_STATUS_T vc_container_filter_control(VC_CONTAINER_FILTER_T *context, VC_CONTAINER_CONTROL_T operation, ... ); + +/* @} */ + +#endif /* VC_CONTAINERS_FILTERS_H */ diff --git a/containers/core/containers_index.c b/containers/core/containers_index.c new file mode 100755 index 0000000..7edebfa --- /dev/null +++ b/containers/core/containers_index.c @@ -0,0 +1,192 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_index.h" + +typedef struct { + int64_t file_offset; + int64_t time; +} VC_CONTAINER_INDEX_POS_T; + +struct VC_CONTAINER_INDEX_T { + int len; // log2 of length of entry array + int next; // next array entry to write into + int gap; // log2 of the passes through entry array to build the full list + int mgap; // len-gap, stored for convenience + int count; // number of calls to index_add since last entry added + int max_count; // log2 of the number of calls to discard between each entry added + int64_t max_time; // time of the latest entry + VC_CONTAINER_INDEX_POS_T entry[0]; // array of position/time pairs +}; + +// We have a fixed length list, and when it is full we want to discard half the entries. +// This is done without coping data by mapping the entry number to the index to the array, +// in the following way: +// Length is a power of two, so the entry number is a fixed constant bit width. The highest gap +// bits are used as a direct offset into the array (o), the lowest mgap bits are right shifted by +// gap to increment this (S). Each time we double the number of passes through the actual array. +// So if len=3, we start off with mgap=3, gap=0, we have a single pass with the trivial mapping: +// |S|S|S| [0 1 2 3 4 5 6 7] +// when this is full we change to mgap=2, gap=1, so we iterate this way: +// |o|S|S| [0 2 4 6] [1 3 5 7] +// when this is full we change to mgap=1, gap=2 +// |o|o|S| [0 4] [1 5] [2 6] [3 7] +// when this is full we change to this, which is equivalent to where we started +// |o|o|o| [0] [1] [2] [3] [4] [5] [6] [7] + +#define ENTRY(x, i) ((x)->gap == 0 ? (i) : ((i)>>(x)->mgap) + (((i) & ((1<<(x)->mgap)-1)) << (x)->gap)) + +VC_CONTAINER_STATUS_T vc_container_index_create( VC_CONTAINER_INDEX_T **index, int length ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + VC_CONTAINER_INDEX_T *id = NULL; + int len = 0; + + if(length < 16) length = 16; + if(length > 4096) length = 4096; + while((length >>= 1) != 0) + len++; + + id = malloc(sizeof(VC_CONTAINER_INDEX_T) + (sizeof(VC_CONTAINER_INDEX_POS_T)<len = id->mgap = len; + + *index = id; + return VC_CONTAINER_SUCCESS; + + error: + return status; +} + +VC_CONTAINER_STATUS_T vc_container_index_free( VC_CONTAINER_INDEX_T *index ) +{ + if(index == NULL) + return VC_CONTAINER_ERROR_FAILED; + + free(index); + return VC_CONTAINER_SUCCESS; +} + +VC_CONTAINER_STATUS_T vc_container_index_add( VC_CONTAINER_INDEX_T *index, int64_t time, int64_t file_offset ) +{ + if(index == NULL) + return VC_CONTAINER_ERROR_FAILED; + + // reject entries if they are in part of the time covered + if(index->next != 0 && time <= index->max_time) + return VC_CONTAINER_SUCCESS; + + index->count++; + if(index->count == (1<max_count)) + { + int entry; + if(index->next == (1<len)) + { + // New entry doesn't fit, we discard every other index record + // by changing how we map index positions to array entry indexes. + index->next >>= 1; + index->gap++; + index->mgap--; + index->max_count++; + + if(index->gap == index->len) + { + index->gap = 0; + index->mgap = index->len; + } + } + + entry = ENTRY(index, index->next); + + index->entry[entry].file_offset = file_offset; + index->entry[entry].time = time; + index->count = 0; + index->next++; + index->max_time = time; + } + + return VC_CONTAINER_SUCCESS; +} + +VC_CONTAINER_STATUS_T vc_container_index_get( VC_CONTAINER_INDEX_T *index, int later, int64_t *time, int64_t *file_offset, int *past ) +{ + int guess, start, end, entry; + + if(index == NULL || index->next == 0) + return VC_CONTAINER_ERROR_FAILED; + + guess = start = 0; + end = index->next-1; + + *past = *time > index->max_time; + + while(end-start > 1) + { + int64_t gtime; + guess = (start+end)>>1; + gtime = index->entry[ENTRY(index, guess)].time; + + if(*time < gtime) + end = guess; + else if(*time > gtime) + start = guess; + else + break; + } + + if (*time != index->entry[ENTRY(index, guess)].time) + { + if(later) + { + if(*time <= index->entry[ENTRY(index, start)].time) + guess = start; + else + guess = end; + } + else + { + if(*time >= index->entry[ENTRY(index, end)].time) + guess = end; + else + guess = start; + } + } + + entry = ENTRY(index, guess); + *time = index->entry[entry].time; + *file_offset = index->entry[entry].file_offset; + + return VC_CONTAINER_SUCCESS; +} + diff --git a/containers/core/containers_index.h b/containers/core/containers_index.h new file mode 100755 index 0000000..f0c49c3 --- /dev/null +++ b/containers/core/containers_index.h @@ -0,0 +1,82 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_INDEX_H +#define VC_CONTAINERS_INDEX_H + +/** \file containers_index.h + * Definition of index utilitie for containers. Creates and maintains an + * index of file offsets and times, and is able to suggest a file position + * to seek to achieve a given time target. Useful for container formats + * that don't include an index. + */ + +#include "containers/containers.h" + +struct VC_CONTAINER_INDEX_T; +typedef struct VC_CONTAINER_INDEX_T VC_CONTAINER_INDEX_T; + +/** + * Creates an index with a suggested number of entries. + * @param index Pointer to created index will be filled here on success. + * @param length Suggested length of index. + * @return Status code + */ +VC_CONTAINER_STATUS_T vc_container_index_create( VC_CONTAINER_INDEX_T **index, int length ); + + +/** + * Frees an index. + * @param index Pointer to valid index. + * @return Status code. + */ +VC_CONTAINER_STATUS_T vc_container_index_free( VC_CONTAINER_INDEX_T *index ); + + +/** + * Adds an entry to the index. If the index is full then some stored records will be + * discarded. + * @param index Pointer to a valid index. + * @param time Timestamp of new index entry. + * @param file_offset File offset for new index entry. + * @return Status code + */ +VC_CONTAINER_STATUS_T vc_container_index_add( VC_CONTAINER_INDEX_T *index, int64_t time, int64_t file_offset ); + + +/** + * Retrieves the best entry for the supplied time offset. + * @param index Pointer to valid index. + * @param later If true, the selected entry is the earliest retained entry with a greater or equal timestamp. + * If false, the selected entry is the latest retained entry with an earlier or equal timestamp. + * @param time The requested time. On success, this is filled in with the time of the selected entry. + * @param file_offset On success, this is filled in with the file offset of the selected entry. + * @param past Set if the requested time is after the last entry in the index. + * @return Status code. + */ +VC_CONTAINER_STATUS_T vc_container_index_get( VC_CONTAINER_INDEX_T *index, int later, int64_t *time, int64_t *file_offset, int *past ); + +#endif /* VC_CONTAINERS_WRITER_UTILS_H */ diff --git a/containers/core/containers_io.c b/containers/core/containers_io.c new file mode 100755 index 0000000..907a2a0 --- /dev/null +++ b/containers/core/containers_io.c @@ -0,0 +1,1088 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_uri.h" + +#define MAX_NUM_CACHED_AREAS 16 +#define MAX_NUM_MEMORY_AREAS 4 +#define NUM_TMP_MEMORY_AREAS 2 +#define MEM_CACHE_READ_MAX_SIZE (32*1024) /* Needs to be a power of 2 */ +#define MEM_CACHE_WRITE_MAX_SIZE (128*1024) /* Needs to be a power of 2 */ +#define MEM_CACHE_TMP_MAX_SIZE (32*1024) /* Needs to be a power of 2 */ +#define MEM_CACHE_ALIGNMENT (1*1024) /* Needs to be a power of 2 */ +#define MEM_CACHE_AREA_READ_MAX_SIZE (4*1024*1024) /* Needs to be a power of 2 */ + +typedef struct VC_CONTAINER_IO_PRIVATE_CACHE_T +{ + int64_t start; /**< Offset to the start of the cached area in the stream */ + int64_t end; /**< Offset to the end of the cached area in the stream */ + + int64_t offset; /**< Offset of the currently cached data in the stream */ + size_t size; /**< Size of the cached area */ + bool dirty; /**< Whether the cache is dirty and needs to be written back */ + + size_t position; /**< Current position in the cache */ + + uint8_t *buffer; /**< Pointer to the start of the valid cache area */ + uint8_t *buffer_end; /**< Pointer to the end of the cache */ + + unsigned int mem_max_size; /**< Maximum size of the memory cache */ + unsigned int mem_size; /**< Size of the memory cache */ + uint8_t *mem; /**< Pointer to the memory cache */ + + VC_CONTAINER_IO_T *io; + +} VC_CONTAINER_IO_PRIVATE_CACHE_T; + +typedef struct VC_CONTAINER_IO_PRIVATE_T +{ + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache; /**< Current cache */ + + unsigned int caches_num; + VC_CONTAINER_IO_PRIVATE_CACHE_T caches; + + unsigned int cached_areas_num; + VC_CONTAINER_IO_PRIVATE_CACHE_T cached_areas[MAX_NUM_CACHED_AREAS]; + + int64_t actual_offset; + + struct VC_CONTAINER_IO_ASYNC_T *async_io; + +} VC_CONTAINER_IO_PRIVATE_T; + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, + VC_CONTAINER_IO_MODE_T mode ); +VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, + VC_CONTAINER_IO_MODE_T mode ); +VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, + VC_CONTAINER_IO_MODE_T mode ); +VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, + VC_CONTAINER_IO_MODE_T mode ); +VC_CONTAINER_STATUS_T vc_container_io_http_open( VC_CONTAINER_IO_T *p_ctx, const char *uri, + VC_CONTAINER_IO_MODE_T mode ); +static VC_CONTAINER_STATUS_T io_seek_not_seekable(VC_CONTAINER_IO_T *p_ctx, int64_t offset); + +static size_t vc_container_io_cache_read( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *data, size_t size ); +static int32_t vc_container_io_cache_write( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, const uint8_t *data, size_t size ); +static VC_CONTAINER_STATUS_T vc_container_io_cache_seek( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int64_t offset ); +static size_t vc_container_io_cache_refill( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ); +static size_t vc_container_io_cache_flush( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ); + +static struct VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T * ); +static VC_CONTAINER_STATUS_T async_io_stop( struct VC_CONTAINER_IO_ASYNC_T *ctx ); +static int async_io_write( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ); +static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ); +static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable ); +static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats ); + +/*****************************************************************************/ +static VC_CONTAINER_IO_T *vc_container_io_open_core( const char *uri, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_IO_CAPABILITIES_T capabilities, + bool b_open, VC_CONTAINER_STATUS_T *p_status ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_T *p_ctx = 0; + VC_CONTAINER_IO_PRIVATE_T *private = 0; + unsigned int uri_length, caches = 0, cache_max_size, num_areas = MAX_NUM_MEMORY_AREAS; + + /* XXX */ + uri_length = strlen(uri) + 1; + + /* Allocate our context before trying out the different io modules */ + p_ctx = malloc( sizeof(*p_ctx) + sizeof(*private) + uri_length); + if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*private) + uri_length ); + p_ctx->priv = private = (VC_CONTAINER_IO_PRIVATE_T *)&p_ctx[1]; + p_ctx->uri = (char *)&private[1]; + memcpy((char *)p_ctx->uri, uri, uri_length); + p_ctx->uri_parts = vc_uri_create(); + if(!p_ctx->uri_parts) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + vc_uri_parse(p_ctx->uri_parts, uri); + + if (b_open) + { + /* Open the actual i/o module */ + status = vc_container_io_null_open(p_ctx, uri, mode); + if(status) status = vc_container_io_net_open(p_ctx, uri, mode); + if(status) status = vc_container_io_pktfile_open(p_ctx, uri, mode); +#ifdef ENABLE_CONTAINER_IO_HTTP + if(status) status = vc_container_io_http_open(p_ctx, uri, mode); +#endif + if(status) status = vc_container_io_file_open(p_ctx, uri, mode); + if(status != VC_CONTAINER_SUCCESS) goto error; + + if(!p_ctx->pf_seek || (p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK)) + { + p_ctx->capabilities |= VC_CONTAINER_IO_CAPS_CANT_SEEK; + p_ctx->pf_seek = io_seek_not_seekable; + } + } + else + { + /* We're only creating an empty container i/o */ + p_ctx->capabilities = capabilities; + } + + if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_NO_CACHING) + caches = 1; + + if(mode == VC_CONTAINER_IO_MODE_WRITE) cache_max_size = MEM_CACHE_WRITE_MAX_SIZE; + else cache_max_size = MEM_CACHE_READ_MAX_SIZE; + + if(mode == VC_CONTAINER_IO_MODE_WRITE && + vc_uri_path_extension(p_ctx->uri_parts) && + !strcasecmp(vc_uri_path_extension(p_ctx->uri_parts), "tmp")) + { + caches = 1; + cache_max_size = MEM_CACHE_TMP_MAX_SIZE; + num_areas = NUM_TMP_MEMORY_AREAS; + } + + /* Check if the I/O needs caching */ + if(caches) + { + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache = &p_ctx->priv->caches; + cache->mem_max_size = cache_max_size; + cache->mem_size = cache->mem_max_size; + cache->io = p_ctx; + cache->mem = malloc(p_ctx->priv->caches.mem_size); + if(cache->mem) + { + cache->buffer = cache->mem; + cache->buffer_end = cache->mem + cache->mem_size; + p_ctx->priv->caches_num = 1; + } + } + + if(p_ctx->priv->caches_num) + p_ctx->priv->cache = &p_ctx->priv->caches; + + + /* Try to start an asynchronous io if we're in write mode and we've got at least 2 cache memory areas */ + if(mode == VC_CONTAINER_IO_MODE_WRITE && p_ctx->priv->cache && num_areas >= 2) + p_ctx->priv->async_io = async_io_start( p_ctx, num_areas, 0 ); + + end: + if(p_status) *p_status = status; + return p_ctx; + + error: + if(p_ctx) vc_uri_release(p_ctx->uri_parts); + if(p_ctx) free(p_ctx); + p_ctx = 0; + goto end; +} + +/*****************************************************************************/ +VC_CONTAINER_IO_T *vc_container_io_open( const char *uri, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_STATUS_T *p_status ) +{ + return vc_container_io_open_core( uri, mode, 0, true, p_status ); +} + +/*****************************************************************************/ +VC_CONTAINER_IO_T *vc_container_io_create( const char *uri, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_IO_CAPABILITIES_T capabilities, + VC_CONTAINER_STATUS_T *p_status ) +{ + return vc_container_io_open_core( uri, mode, capabilities, false, p_status ); +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_close( VC_CONTAINER_IO_T *p_ctx ) +{ + unsigned int i; + + if(p_ctx) + { + if(p_ctx->priv) + { + if(p_ctx->priv->caches_num) + { + if(p_ctx->priv->caches.dirty) + vc_container_io_cache_flush( p_ctx, &p_ctx->priv->caches, 1 ); + } + + if(p_ctx->priv->async_io) + async_io_stop( p_ctx->priv->async_io ); + else if(p_ctx->priv->caches_num) + free(p_ctx->priv->caches.mem); + + for(i = 0; i < p_ctx->priv->cached_areas_num; i++) + free(p_ctx->priv->cached_areas[i].mem); + + if(p_ctx->pf_close) + p_ctx->pf_close(p_ctx); + } + vc_uri_release(p_ctx->uri_parts); + free(p_ctx); + } + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +size_t vc_container_io_peek(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + size_t ret; + + if(p_ctx->priv->cache) + { + /* FIXME: do something a bit more clever than this */ + int64_t offset = p_ctx->offset; + ret = vc_container_io_read(p_ctx, buffer, size); + vc_container_io_seek(p_ctx, offset); + return ret; + } + + if (p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK) + return 0; + + ret = p_ctx->pf_read(p_ctx, buffer, size); + p_ctx->pf_seek(p_ctx, p_ctx->offset); + return ret; +} + +/*****************************************************************************/ +size_t vc_container_io_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + size_t ret; + + if(p_ctx->priv->cache) + ret = vc_container_io_cache_read( p_ctx, p_ctx->priv->cache, (uint8_t*)buffer, size ); + else + { + ret = p_ctx->pf_read(p_ctx, buffer, size); + p_ctx->priv->actual_offset += ret; + } + + p_ctx->offset += ret; + return ret; +} + +/*****************************************************************************/ +size_t vc_container_io_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + int32_t ret; + + if(p_ctx->priv->cache) + ret = vc_container_io_cache_write( p_ctx, p_ctx->priv->cache, (const uint8_t*)buffer, size ); + else + { + ret = p_ctx->pf_write(p_ctx, buffer, size); + p_ctx->priv->actual_offset += ret; + } + + p_ctx->offset += ret; + return ret < 0 ? 0 : ret; +} + +/*****************************************************************************/ +size_t vc_container_io_skip(VC_CONTAINER_IO_T *p_ctx, size_t size) +{ + if(!size) return 0; + + if(size < 8) + { + uint8_t value[8]; + return vc_container_io_read(p_ctx, value, size); + } + + if(p_ctx->priv->cache) + { + if(vc_container_io_cache_seek(p_ctx, p_ctx->priv->cache, p_ctx->offset + size)) return 0; + p_ctx->offset += size; + return size; + } + + if(vc_container_io_seek(p_ctx, p_ctx->offset + size)) return 0; + return size; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_STATUS_T status; + unsigned int i; + + /* Check if the requested position is in one of the cached areas */ + for(i = 0; i < p_ctx->priv->cached_areas_num; i++) + { + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache = &p_ctx->priv->cached_areas[i]; + if(offset >= cache->start && offset < cache->end) + { + p_ctx->priv->cache = cache; + break; + } + } + if(i == p_ctx->priv->cached_areas_num) + p_ctx->priv->cache = p_ctx->priv->caches_num ? &p_ctx->priv->caches : 0; + + if(p_ctx->priv->cache) + { + status = vc_container_io_cache_seek( p_ctx, p_ctx->priv->cache, offset ); + if(status == VC_CONTAINER_SUCCESS) p_ctx->offset = offset; + return status; + } + + if(p_ctx->status == VC_CONTAINER_SUCCESS && + offset == p_ctx->offset) return VC_CONTAINER_SUCCESS; + + status = p_ctx->pf_seek(p_ctx, offset); + if(status == VC_CONTAINER_SUCCESS) p_ctx->offset = offset; + p_ctx->priv->actual_offset = p_ctx->offset; + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_seek_not_seekable(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_IO_PRIVATE_T *private = p_ctx->priv; + + vc_container_assert(offset >= private->actual_offset); + if(offset == private->actual_offset) return VC_CONTAINER_SUCCESS; + + if(offset < private->actual_offset) + { + p_ctx->status = VC_CONTAINER_ERROR_EOS; + return p_ctx->status; + } + + offset -= private->actual_offset; + while(offset && !p_ctx->status) + { + uint8_t value[64]; + unsigned int ret, size = MIN(offset, 64); + ret = p_ctx->pf_read(p_ctx, value, size); + if(ret != size) p_ctx->status = VC_CONTAINER_ERROR_EOS; + offset -= ret; + } + return p_ctx->status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_control_list(VC_CONTAINER_IO_T *context, VC_CONTAINER_CONTROL_T operation, va_list args) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + if (context->pf_control) + status = context->pf_control(context, operation, args); + + /* Option to add generic I/O control here */ + + if(operation == VC_CONTAINER_CONTROL_IO_FLUSH && context->priv->cache) + { + status = VC_CONTAINER_SUCCESS; + (void)vc_container_io_cache_flush( context, context->priv->cache, 1 ); + } + + if(operation == VC_CONTAINER_CONTROL_SET_IO_PERF_STATS && context->priv->async_io) + { + status = VC_CONTAINER_SUCCESS; + async_io_stats_initialise(context->priv->async_io, va_arg(args, int)); + } + + if(operation == VC_CONTAINER_CONTROL_GET_IO_PERF_STATS && context->priv->async_io) + { + status = VC_CONTAINER_SUCCESS; + async_io_stats_get(context->priv->async_io, va_arg(args, VC_CONTAINER_WRITE_STATS_T *)); + } + + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_control(VC_CONTAINER_IO_T *context, VC_CONTAINER_CONTROL_T operation, ...) +{ + VC_CONTAINER_STATUS_T result; + va_list args; + + va_start(args, operation); + result = vc_container_io_control_list(context, operation, args); + va_end(args); + + return result; +} + +/*****************************************************************************/ +size_t vc_container_io_cache(VC_CONTAINER_IO_T *p_ctx, size_t size) +{ + VC_CONTAINER_IO_PRIVATE_T *private = p_ctx->priv; + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, *main_cache; + VC_CONTAINER_STATUS_T status; + + /* Sanity checking */ + if(private->cached_areas_num >= MAX_NUM_CACHED_AREAS) return 0; + + cache = &private->cached_areas[private->cached_areas_num]; + cache->start = p_ctx->offset; + cache->end = cache->start + size; + cache->offset = p_ctx->offset; + cache->position = 0; + cache->size = 0; + cache->io = p_ctx; + + /* Set the size of the cache area depending on the capabilities of the i/o */ + if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK) + cache->mem_max_size = MEM_CACHE_AREA_READ_MAX_SIZE; + else if((p_ctx->capabilities & VC_CONTAINER_IO_CAPS_SEEK_SLOW) && + size <= MEM_CACHE_AREA_READ_MAX_SIZE) + cache->mem_max_size = MEM_CACHE_AREA_READ_MAX_SIZE; + else + cache->mem_max_size = MEM_CACHE_READ_MAX_SIZE; + + cache->mem_size = size; + if(cache->mem_size > cache->mem_max_size) cache->mem_size = cache->mem_max_size; + cache->mem = malloc(cache->mem_size); + + cache->buffer = cache->mem; + cache->buffer_end = cache->mem + cache->mem_size; + + if(!cache->mem) return 0; + private->cached_areas_num++; + + /* Copy any data we've got in the current cache into the new cache */ + main_cache = p_ctx->priv->cache; + if(main_cache && main_cache->position < main_cache->size) + { + cache->size = main_cache->size - main_cache->position; + if(cache->size > cache->mem_size) cache->size = cache->mem_size; + memcpy(cache->buffer, main_cache->buffer + main_cache->position, cache->size); + main_cache->position += cache->size; + } + + /* Read the rest of the cache directly from the stream */ + if(cache->mem_size > cache->size) + { + size_t ret = cache->io->pf_read(cache->io, cache->buffer + cache->size, + cache->mem_size - cache->size); + cache->size += ret; + cache->io->priv->actual_offset = cache->offset + cache->size; + } + + status = vc_container_io_seek(p_ctx, cache->end); + if(status != VC_CONTAINER_SUCCESS) + return 0; + + if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK) + return cache->size; + else + return size; +} + +/*****************************************************************************/ +static size_t vc_container_io_cache_refill( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ) +{ + size_t ret = vc_container_io_cache_flush( p_ctx, cache, 1 ); + + if(ret) return 0; /* TODO what should we do there ? */ + + if(p_ctx->priv->actual_offset != cache->offset) + { + if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS) + return 0; + } + + ret = cache->io->pf_read(cache->io, cache->buffer, cache->buffer_end - cache->buffer); + cache->size = ret; + cache->position = 0; + cache->io->priv->actual_offset = cache->offset + ret; + return ret; +} + +/*****************************************************************************/ +static size_t vc_container_io_cache_refill_bypass( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *buffer, size_t size ) +{ + size_t ret = vc_container_io_cache_flush( p_ctx, cache, 1 ); + + if(ret) return 0; /* TODO what should we do there ? */ + + if(p_ctx->priv->actual_offset != cache->offset) + { + if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS) + return 0; + } + + ret = cache->io->pf_read(cache->io, buffer, size); + cache->size = cache->position = 0; + cache->offset += ret; + cache->io->priv->actual_offset = cache->offset; + return ret; +} + +/*****************************************************************************/ +static size_t vc_container_io_cache_read( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *data, size_t size ) +{ + size_t read = 0, bytes, ret; + + while(size) + { + bytes = cache->size - cache->position; /* Bytes left in cache */ + +#if 1 // FIXME Only if stream is seekable + /* Try to read directly from the stream if the cache just gets in the way */ + if(!bytes && size > cache->mem_size) + { + bytes = cache->mem_size; + ret = vc_container_io_cache_refill_bypass( p_ctx, cache, data + read, bytes); + read += ret; + + if(ret != bytes) /* We didn't read as many bytes as we had hoped */ + goto end; + + size -= bytes; + continue; + } +#endif + + /* Refill the cache if it is empty */ + if(!bytes) bytes = vc_container_io_cache_refill( p_ctx, cache ); + if(!bytes) goto end; + + /* We do have some data in the cache so override the status */ + p_ctx->status = VC_CONTAINER_SUCCESS; + + /* Read data directly from the cache */ + if(bytes > size) bytes = size; + memcpy(data + read, cache->buffer + cache->position, bytes); + cache->position += bytes; + read += bytes; + size -= bytes; + } + + end: + vc_container_assert(cache->offset + cache->position == p_ctx->offset + read); + return read; +} + +/*****************************************************************************/ +static int32_t vc_container_io_cache_write( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, const uint8_t *data, size_t size ) +{ + int32_t written = 0; + size_t bytes, ret; + + /* If we do not have a write cache then we need to flush it */ + if(cache->size && !cache->dirty) + { + ret = vc_container_io_cache_flush( p_ctx, cache, 1 ); + if(ret) return -(int32_t)ret; + } + + while(size) + { + bytes = (cache->buffer_end - cache->buffer) - cache->position; /* Space left in cache */ + + /* Flush the cache if it is full */ + if(!bytes) + { + /* Cache full, flush it */ + ret = vc_container_io_cache_flush( p_ctx, cache, 0 ); + if(ret) + { + written -= ret; + return written; + } + continue; + } + + if(bytes > size) bytes = size; + + if(!p_ctx->priv->async_io && bytes == cache->mem_size) + { + /* Write directly from the buffer */ + ret = cache->io->pf_write(cache->io, data + written, bytes); + cache->offset += ret; + cache->io->priv->actual_offset += ret; + } + else + { + /* Write in the cache */ + memcpy(cache->buffer + cache->position, data + written, bytes); + cache->position += bytes; + cache->dirty = 1; + ret = bytes; + } + + written += ret; + if(ret != bytes) goto end; + + size -= bytes; + } + + end: + vc_container_assert(cache->offset + (int64_t)cache->position == p_ctx->offset + written); + if(cache->position > cache->size) cache->size = cache->position; + return written; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T vc_container_io_cache_seek(VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int64_t offset) +{ + VC_CONTAINER_STATUS_T status; + size_t shift, ret; + + /* Check if the seek position is within our cache */ + if(offset >= cache->offset && offset < cache->offset + (int64_t)cache->size) + { + cache->position = offset - cache->offset; + return VC_CONTAINER_SUCCESS; + } + + shift = cache->buffer - cache->mem; + if(!cache->dirty && shift && cache->size && + offset >= cache->offset - (int64_t)shift && offset < cache->offset) + { + /* We need to refill the partial bit of the cache that we didn't take care of last time */ + status = cache->io->pf_seek(cache->io, cache->offset - shift); + if(status != VC_CONTAINER_SUCCESS) return status; + cache->offset -= shift; + cache->buffer -= shift; + + ret = cache->io->pf_read(cache->io, cache->buffer, shift); + vc_container_assert(ret == shift); /* FIXME: ret must = shift */ + cache->size += shift; + cache->position = offset - cache->offset; + cache->io->priv->actual_offset = cache->offset + ret; + return VC_CONTAINER_SUCCESS; + } + + if(cache->dirty) vc_container_io_cache_flush( p_ctx, cache, 1 ); + // FIXME: what if all the data couldn't be flushed ? + + if(p_ctx->priv->async_io) async_io_wait_complete( p_ctx->priv->async_io, cache, 1 ); + + status = cache->io->pf_seek(cache->io, offset); + if(status != VC_CONTAINER_SUCCESS) return status; + + vc_container_io_cache_flush( p_ctx, cache, 1 ); + + cache->offset = offset; + cache->io->priv->actual_offset = offset; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t vc_container_io_cache_flush( VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ) +{ + size_t ret = 0, shift; + + if(cache->position > cache->size) cache->size = cache->position; + + if(cache->dirty && cache->size) + { + if(p_ctx->priv->actual_offset != cache->offset) + { + if(p_ctx->priv->async_io) async_io_wait_complete( p_ctx->priv->async_io, cache, complete ); + + if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS) + return 0; + } + + if(p_ctx->priv->async_io) + { + ret = async_io_write( p_ctx->priv->async_io, cache ); + if(async_io_wait_complete( p_ctx->priv->async_io, cache, complete ) != VC_CONTAINER_SUCCESS) + ret = 0; + } + else + ret = cache->io->pf_write(cache->io, cache->buffer, cache->size); + + cache->io->priv->actual_offset = cache->offset + ret; + ret = cache->position - ret; + } + cache->dirty = 0; + + cache->offset += cache->size; + if(cache->mem_size == cache->mem_max_size) + { + shift = cache->offset &(MEM_CACHE_ALIGNMENT-1); + cache->buffer = cache->mem + shift; + } + + cache->position = cache->size = 0; + return ret; +} + +/***************************************************************************** + * Asynchronous I/O. + * This is here to keep the I/O as busy as possible by allowing the writer + * to continue its work while the I/O is taking place in the background. + *****************************************************************************/ + +#ifdef ENABLE_CONTAINERS_ASYNC_IO +#include "vcos.h" + +#define NUMPC(c,n,s) ((c) < (1<<(s)) ? (n) : ((n) / (c >> (s)))) + +static void stats_initialise(VC_CONTAINER_STATS_T *st, uint32_t shift) +{ + memset(st, 0, sizeof(VC_CONTAINER_STATS_T)); + st->shift = shift; +} + +static void stats_add_value(VC_CONTAINER_STATS_T *st, uint32_t count, uint32_t num) +{ + uint32_t numpc; + int i, j; + + if(count == 0) + return; + + numpc = NUMPC(count, num, st->shift); + // insert in the right place + i=0; + while(i < VC_CONTAINER_STATS_BINS && st->record[i].count != 0 && st->record[i].numpc > numpc) + i++; + + if(st->record[i].count != 0 && st->record[i].numpc == numpc) + { + // equal numpc, can merge now + st->record[i].count += count; + st->record[i].num += num; + } + else + { + // shift higher records up + for(j=VC_CONTAINER_STATS_BINS; j>i; j--) + st->record[j] = st->record[j-1]; + + // write record in + st->record[i].count = count; + st->record[i].num = num; + st->record[i].numpc = numpc; + + // if full, join the two closest records + if(st->record[VC_CONTAINER_STATS_BINS].count) + { + uint32_t min_diff = 0; + j = -1; + + // find closest, based on difference between numpc + for(i=0; irecord[i].numpc - st->record[i+1].numpc; + if(j == -1 || diff < min_diff) + { + j = i; + min_diff = diff; + } + } + + // merge these records + st->record[j].count += st->record[j+1].count; + st->record[j].num += st->record[j+1].num; + st->record[j].numpc = NUMPC(st->record[j].count, st->record[j].num, st->shift); + + // shift down higher records + while(++j < VC_CONTAINER_STATS_BINS) + st->record[j] = st->record[j+1]; + + // zero the free top record + st->record[VC_CONTAINER_STATS_BINS].count = 0; + st->record[VC_CONTAINER_STATS_BINS].num = 0; + st->record[VC_CONTAINER_STATS_BINS].numpc = 0; + } + } +} + +typedef struct VC_CONTAINER_IO_ASYNC_T +{ + VC_CONTAINER_IO_T *io; + VCOS_THREAD_T thread; + VCOS_SEMAPHORE_T spare_sema; + VCOS_SEMAPHORE_T queue_sema; + VCOS_EVENT_T wake_event; + int quit; + + unsigned int num_area; + uint8_t *mem[MAX_NUM_MEMORY_AREAS]; /**< Base address of memory areas */ + uint8_t *buffer[MAX_NUM_MEMORY_AREAS]; /**< When queued for writing, pointer to start of valid cache area */ + size_t size[MAX_NUM_MEMORY_AREAS]; /**< When queued for writing, size of valid area to write */ + unsigned int cur_area; + + unsigned char stack[3000]; + int error; + + int stats_enable; + VC_CONTAINER_WRITE_STATS_T stats; + +} VC_CONTAINER_IO_ASYNC_T; + +/*****************************************************************************/ +static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable ) +{ + ctx->stats_enable = enable; + stats_initialise(&ctx->stats.write, 8); + stats_initialise(&ctx->stats.wait, 0); + stats_initialise(&ctx->stats.flush, 0); +} + +static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats ) +{ + *stats = ctx->stats; +} + +static void *async_io_thread(VOID *argv) +{ + VC_CONTAINER_IO_ASYNC_T *ctx = argv; + unsigned int write_area = 0; + + while (1) + { + unsigned long time = 0; + + vcos_event_wait(&ctx->wake_event); + if(ctx->quit) break; + + while(vcos_semaphore_trywait(&ctx->queue_sema) == VCOS_SUCCESS) + { + uint8_t *buffer = ctx->buffer[write_area]; + size_t size = ctx->size[write_area]; + + if(ctx->stats_enable) + time = vcos_getmicrosecs(); + + if(ctx->io->pf_write(ctx->io, buffer, size) != size) + ctx->error = 1; + + if(ctx->stats_enable) + stats_add_value(&ctx->stats.write, size, vcos_getmicrosecs() - time); + + /* Signal that the write is done */ + vcos_semaphore_post(&ctx->spare_sema); + + if(++write_area == ctx->num_area) + write_area = 0; + } + } + + return NULL; +} + +static int async_io_write( VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ) +{ + unsigned long time = 0; + unsigned int offset; + + if(ctx->stats_enable) + time = vcos_getmicrosecs(); + + /* post the current area */ + ctx->buffer[ctx->cur_area] = cache->buffer; + ctx->size[ctx->cur_area] = cache->size; + vcos_semaphore_post(&ctx->queue_sema); + vcos_event_signal(&ctx->wake_event); + + /* now we need to grab another area */ + vcos_semaphore_wait(&ctx->spare_sema); + if(++ctx->cur_area == ctx->num_area) + ctx->cur_area = 0; + + if(ctx->stats_enable) + stats_add_value(&ctx->stats.wait, 1, vcos_getmicrosecs() - time); + + /* alter cache mem to point to the new cur_area */ + offset = cache->buffer - cache->mem; + cache->mem = ctx->mem[ctx->cur_area]; + cache->buffer = cache->mem + offset; + cache->buffer_end = cache->mem + cache->mem_size; + + return ctx->error ? 0 : cache->size; +} + +static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ) +{ + unsigned int time = 0; + + if(ctx->stats_enable) + time = vcos_getmicrosecs(); + + if(complete) + { + int num; + /* Need to make sure that all memory areas have been written out, so should have num-1 spare */ + for(num=0; numnum_area-1; num++) + vcos_semaphore_wait(&ctx->spare_sema); + + for(num=0; numnum_area-1; num++) + vcos_semaphore_post(&ctx->spare_sema); + } + else + { + /* Need to make sure we can acquire one memory area */ + vcos_semaphore_wait(&ctx->spare_sema); + vcos_semaphore_post(&ctx->spare_sema); + } + + if(ctx->stats_enable) + stats_add_value(&ctx->stats.flush, 1, vcos_getmicrosecs() - time); + + return ctx->error ? VC_CONTAINER_ERROR_FAILED : VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T *status ) +{ + VC_CONTAINER_IO_ASYNC_T *ctx = 0; + VCOS_UNSIGNED pri = 0; + + /* Allocate our context */ + ctx = malloc(sizeof(*ctx)); + if(!ctx) goto error_spare_sema; + memset(ctx, 0, sizeof(*ctx)); + ctx->io = io; + + ctx->mem[0] = io->priv->cache->mem; + + for(ctx->num_area = 1; ctx->num_area < num_areas; ctx->num_area++) + { + ctx->mem[ctx->num_area] = malloc(io->priv->cache->mem_size); + if(!ctx->mem[ctx->num_area]) + break; + } + + if(ctx->num_area == 1) // no real benefit in asynchronous writes + goto error_spare_sema; + + async_io_stats_initialise(ctx, 0); + + if(vcos_semaphore_create(&ctx->spare_sema, "async_spare_sem", ctx->num_area-1) != VCOS_SUCCESS) + goto error_spare_sema; + + if(vcos_semaphore_create(&ctx->queue_sema, "async_queue_sem", 0) != VCOS_SUCCESS) + goto error_queue_sema; + + if (vcos_event_create(&ctx->wake_event, "async_wake_event") != VCOS_SUCCESS) + goto error_event; + + // run this thread at a slightly higher priority than the calling thread - that means that + // we prefer to write to the SD card rather than filling the memory buffer. + pri = vcos_thread_get_priority(vcos_thread_current()); + if(vcos_thread_create_classic(&ctx->thread, "async_io", async_io_thread, ctx, + ctx->stack, sizeof(ctx->stack), pri-1, 10, VCOS_START) != VCOS_SUCCESS) + goto error_thread; + + if(status) *status = VC_CONTAINER_SUCCESS; + return ctx; + + error_thread: + vcos_event_delete(&ctx->wake_event); + error_event: + vcos_semaphore_delete(&ctx->queue_sema); + error_queue_sema: + vcos_semaphore_delete(&ctx->spare_sema); + error_spare_sema: + if(ctx) free(ctx); + if(status) *status = VC_CONTAINER_ERROR_FAILED; + return 0; +} + +static VC_CONTAINER_STATUS_T async_io_stop( VC_CONTAINER_IO_ASYNC_T *ctx ) +{ + /* Block if a write operation is already in progress */ + //vcos_semaphore_wait(&ctx->sema); + // XXX block until all done + + ctx->quit = 1; + vcos_event_signal(&ctx->wake_event); + vcos_thread_join(&ctx->thread,NULL); + vcos_event_delete(&ctx->wake_event); + vcos_semaphore_delete(&ctx->queue_sema); + vcos_semaphore_delete(&ctx->spare_sema); + + while(ctx->num_area > 0) + free(ctx->mem[--ctx->num_area]); + + free(ctx); + return VC_CONTAINER_SUCCESS; +} +#else + +static struct VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T *status ) +{ + VC_CONTAINER_PARAM_UNUSED(io); + VC_CONTAINER_PARAM_UNUSED(num_areas); + if(status) *status = VC_CONTAINER_ERROR_FAILED; + return 0; +} + +static int async_io_write( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache ) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + VC_CONTAINER_PARAM_UNUSED(cache); + return 0; +} + +static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx, + VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete ) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + VC_CONTAINER_PARAM_UNUSED(cache); + VC_CONTAINER_PARAM_UNUSED(complete); + return 0; +} + +static VC_CONTAINER_STATUS_T async_io_stop( struct VC_CONTAINER_IO_ASYNC_T *ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + return VC_CONTAINER_SUCCESS; +} + +static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable ) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + VC_CONTAINER_PARAM_UNUSED(enable); +} + +static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats ) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + VC_CONTAINER_PARAM_UNUSED(stats); +} + + +#endif diff --git a/containers/core/containers_io.h b/containers/core/containers_io.h new file mode 100755 index 0000000..8cd4407 --- /dev/null +++ b/containers/core/containers_io.h @@ -0,0 +1,229 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_IO_H +#define VC_CONTAINERS_IO_H + +/** \file containers_io.h + * Interface definition for the input / output abstraction layer used by container + * readers and writers */ +#include "containers/containers.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup VcContainerIoApi Container I/O API */ +/* @{ */ + +/** Container io opening mode. + * This is used to specify whether a reader or writer is requested. + */ +typedef enum { + VC_CONTAINER_IO_MODE_READ = 0, /**< Container io opened in reading mode */ + VC_CONTAINER_IO_MODE_WRITE = 1 /**< Container io opened in writing mode */ +} VC_CONTAINER_IO_MODE_T; + +/** \name Container I/O Capabilities + * The following flags are exported by container i/o modules to describe their capabilities */ +/* @{ */ +/** Type definition for container I/O capabilities */ +typedef uint32_t VC_CONTAINER_IO_CAPABILITIES_T; +/** Seeking is not supported */ +#define VC_CONTAINER_IO_CAPS_CANT_SEEK 0x1 +/** Seeking is slow and should be avoided */ +#define VC_CONTAINER_IO_CAPS_SEEK_SLOW 0x2 +/** The I/O doesn't provide any caching of the data */ +#define VC_CONTAINER_IO_CAPS_NO_CACHING 0x4 +/* @} */ + +/** Container Input / Output Context. + * This structure defines the context for a container io instance */ +struct VC_CONTAINER_IO_T +{ + /** Pointer to information private to the container io instance */ + struct VC_CONTAINER_IO_PRIVATE_T *priv; + + /** Pointer to information private to the container io module */ + struct VC_CONTAINER_IO_MODULE_T *module; + + /** Uniform Resource Identifier for the stream to open. + * This is a string encoded in UTF-8 which follows the syntax defined in + * RFC2396 (http://tools.ietf.org/html/rfc2396). */ + char *uri; + + /** Pre-parsed URI */ + struct VC_URI_PARTS_T *uri_parts; + + /** Current offset into the i/o stream */ + int64_t offset; + + /** Current size of the i/o stream (0 if unknown). This size might grow during the + * lifetime of the i/o instance (for instance when doing progressive download). */ + int64_t size; + + /** Capabilities of the i/o stream */ + VC_CONTAINER_IO_CAPABILITIES_T capabilities; + + /** Status of the i/o stream */ + VC_CONTAINER_STATUS_T status; + + /** Maximum size allowed for this i/o stream (0 if unknown). This is used during writing + * to limit the size of the stream to below this value. */ + int64_t max_size; + + /** \note the following list of function pointers should not be used directly. + * They defines the interface for implementing container io modules and are filled in + * by the container modules themselves. */ + + /** \private + * Function pointer to close and free all resources allocated by a + * container io module */ + VC_CONTAINER_STATUS_T (*pf_close)(struct VC_CONTAINER_IO_T *io); + + /** \private + * Function pointer to read or skip data from container io module */ + size_t (*pf_read)(struct VC_CONTAINER_IO_T *io, void *buffer, size_t size); + + /** \private + * Function pointer to write data to a container io module */ + size_t (*pf_write)(struct VC_CONTAINER_IO_T *io, const void *buffer, size_t size); + + /** \private + * Function pointer to seek into a container io module */ + VC_CONTAINER_STATUS_T (*pf_seek)(struct VC_CONTAINER_IO_T *io, int64_t offset); + + /** \private + * Function pointer to perform a control operation on a container io module */ + VC_CONTAINER_STATUS_T (*pf_control)(struct VC_CONTAINER_IO_T *io, + VC_CONTAINER_CONTROL_T operation, va_list args); + +}; + +/** Opens an i/o stream pointed to by a URI. + * This will create an instance of the container i/o module. + * + * \param uri Uniform Resource Identifier pointing to the multimedia container + * \param mode Mode in which the i/o stream will be opened + * \param status Returns the status of the operation + * \return If successful, this returns a pointer to the new instance + * of the i/o module. Returns NULL on failure. + */ +VC_CONTAINER_IO_T *vc_container_io_open( const char *uri, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_STATUS_T *status ); + +/** Creates an empty i/o stream. The i/o function pointers will have to be set + * by the caller before the i/o gets used. + * This will create an instance of the container i/o module. + * + * \param uri Uniform Resource Identifier pointing to the multimedia container + * \param mode Mode in which the i/o stream will be opened + * \param capabilities Flags indicating the capabilities of the i/o + * \param status Returns the status of the operation + * \return If successful, this returns a pointer to the new instance + * of the i/o module. Returns NULL on failure. + */ +VC_CONTAINER_IO_T *vc_container_io_create( const char *uri, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_IO_CAPABILITIES_T capabilities, + VC_CONTAINER_STATUS_T *p_status ); + +/** Closes an instance of a container i/o module. + * \param context Pointer to the VC_CONTAINER_IO_T context of the instance to close + * \return VC_CONTAINER_SUCCESS on success. + */ +VC_CONTAINER_STATUS_T vc_container_io_close( VC_CONTAINER_IO_T *context ); + +/** Read data from an i/o stream without advancing the read position within the stream. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param buffer Pointer to the buffer where the data will be read + * \param size Number of bytes to read + * \return The size of the data actually read. + */ +size_t vc_container_io_peek(VC_CONTAINER_IO_T *context, void *buffer, size_t size); + +/** Read data from an i/o stream. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param buffer Pointer to the buffer where the data will be read + * \param size Number of bytes to read + * \return The size of the data actually read. + */ +size_t vc_container_io_read(VC_CONTAINER_IO_T *context, void *buffer, size_t size); + +/** Skip data in an i/o stream without reading it. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param size Number of bytes to skip + * \return The size of the data actually skipped. + */ +size_t vc_container_io_skip(VC_CONTAINER_IO_T *context, size_t size); + +/** Write data to an i/o stream. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param buffer Pointer to the buffer containing the data to write + * \param size Number of bytes to write + * \return The size of the data actually written. + */ +size_t vc_container_io_write(VC_CONTAINER_IO_T *context, const void *buffer, size_t size); + +/** Seek into an i/o stream. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param offset Absolute file offset to seek to + * \return Status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_io_seek(VC_CONTAINER_IO_T *context, int64_t offset); + +/** Perform control operation on an i/o stream (va_list). + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param operation Control operation to be performed + * \param args Additional arguments for the operation + * \return Status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_io_control_list(VC_CONTAINER_IO_T *context, + VC_CONTAINER_CONTROL_T operation, va_list args); + +/** Perform control operation on an i/o stream (varargs). + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param operation Control operation to be performed + * \param ... Additional arguments for the operation + * \return Status of the operation + */ +VC_CONTAINER_STATUS_T vc_container_io_control(VC_CONTAINER_IO_T *context, + VC_CONTAINER_CONTROL_T operation, ...); + +/** Cache the pointed region of the i/o stream (from current position). + * This will allow future seeking into the specified region even on non-seekable streams. + * \param context Pointer to the VC_CONTAINER_IO_T instance to use + * \param size Size of the region to cache + * \return Status of the operation + */ +size_t vc_container_io_cache(VC_CONTAINER_IO_T *context, size_t size); + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* VC_CONTAINERS_HELPERS_H */ diff --git a/containers/core/containers_io_helpers.c b/containers/core/containers_io_helpers.c new file mode 100755 index 0000000..88e08cc --- /dev/null +++ b/containers/core/containers_io_helpers.c @@ -0,0 +1,244 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_logging.h" + +void vc_container_helper_format_debug(VC_CONTAINER_T *ctx, int indent, const char *format, ...) +{ + char debug_string[512]; + va_list args; + int result; + + if(indent >= (int)sizeof(debug_string)) return; + memset(debug_string, ' ', indent); + + va_start( args, format ); + result = vsnprintf(debug_string + indent, sizeof(debug_string) - indent, format, args); + va_end( args ); + + if(result <= 0) return; + + vc_container_log(ctx, VC_CONTAINER_LOG_FORMAT, debug_string); + fflush(0); +} + +uint64_t vc_container_helper_int_debug(VC_CONTAINER_T *ctx, int type, uint64_t value, const char *name, int indent) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + + if(type == LOG_FORMAT_TYPE_HEX) + vc_container_helper_format_debug(ctx, indent, "%s: 0x%"PRIx64, name, value); + else + vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value); + return value; +} + +uint64_t vc_container_helper_read_debug(VC_CONTAINER_T *ctx, int type, int size, + const char *name, uint8_t *buffer, int indent, int b_skip) +{ + int64_t offset = STREAM_POSITION(ctx); + uint64_t value = 0; + GUID_T guid; + + if(type == LOG_FORMAT_TYPE_STRING || + type == LOG_FORMAT_TYPE_STRING_UTF16_LE || + type == LOG_FORMAT_TYPE_STRING_UTF16_BE) + { + uint8_t stringbuf[256]; + char utf8buf[256]; + int stringsize = sizeof(stringbuf) - 2; + + if(!buffer) + { + buffer = stringbuf; + if(size < stringsize) stringsize = size; + } + else stringsize = size; + + value = vc_container_io_read(ctx->priv->io, buffer, stringsize); + + if(!utf8_from_charset(type == LOG_FORMAT_TYPE_STRING ? "UTF8" : "UTF16-LE", + utf8buf, sizeof(utf8buf), buffer, stringsize)) + vc_container_helper_format_debug(ctx, indent, "%s: \"%s\"", name, utf8buf); + else + vc_container_helper_format_debug(ctx, indent, "%s: (could not read)", name); + + if(size - stringsize) + value += vc_container_io_skip(ctx->priv->io, size - stringsize); + return value; + } + + if(type == LOG_FORMAT_TYPE_UINT_LE) + { + switch(size) + { + case 1: value = vc_container_io_read_uint8(ctx->priv->io); break; + case 2: value = vc_container_io_read_le_uint16(ctx->priv->io); break; + case 3: value = vc_container_io_read_le_uint24(ctx->priv->io); break; + case 4: value = vc_container_io_read_le_uint32(ctx->priv->io); break; + case 5: value = vc_container_io_read_le_uint40(ctx->priv->io); break; + case 6: value = vc_container_io_read_le_uint48(ctx->priv->io); break; + case 7: value = vc_container_io_read_le_uint56(ctx->priv->io); break; + case 8: value = vc_container_io_read_le_uint64(ctx->priv->io); break; + } + } + else if(type == LOG_FORMAT_TYPE_UINT_BE) + { + switch(size) + { + case 1: value = vc_container_io_read_uint8(ctx->priv->io); break; + case 2: value = vc_container_io_read_be_uint16(ctx->priv->io); break; + case 3: value = vc_container_io_read_be_uint24(ctx->priv->io); break; + case 4: value = vc_container_io_read_be_uint32(ctx->priv->io); break; + case 5: value = vc_container_io_read_be_uint40(ctx->priv->io); break; + case 6: value = vc_container_io_read_be_uint48(ctx->priv->io); break; + case 7: value = vc_container_io_read_be_uint56(ctx->priv->io); break; + case 8: value = vc_container_io_read_be_uint64(ctx->priv->io); break; + } + } + else if(type == LOG_FORMAT_TYPE_FOURCC) + { + value = vc_container_io_read_fourcc(ctx->priv->io); + } + else if(type == LOG_FORMAT_TYPE_GUID) + { + value = vc_container_io_read(ctx->priv->io, &guid, 16); + } + else + { + vc_container_assert(0); + return 0; + } + + if(type == LOG_FORMAT_TYPE_GUID) + { + if(value == 16) + { + vc_container_helper_format_debug(ctx, indent, "%s: 0x%x-0x%x-0x%x-0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", + name, guid.word0, guid.short0, guid.short1, + guid.bytes[0], guid.bytes[1], guid.bytes[2], guid.bytes[3], + guid.bytes[4], guid.bytes[5], guid.bytes[6], guid.bytes[7]); + if(buffer) memcpy(buffer, &guid, sizeof(guid)); + } + } + else if(type == LOG_FORMAT_TYPE_FOURCC) + { + uint32_t val = value; + vc_container_helper_format_debug(ctx, indent, "%s: %4.4s", name, (char *)&val); + } + else + { + vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value); + } + + if(b_skip) value = (STREAM_POSITION(ctx) - offset) != size; + return value; +} + +VC_CONTAINER_STATUS_T vc_container_helper_write_debug(VC_CONTAINER_T *ctx, int type, int size, + const char *name, uint64_t value, const uint8_t *buffer, int indent, int silent) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if(type == LOG_FORMAT_TYPE_STRING) + { + value = vc_container_io_write(ctx->priv->io, buffer, size); + if(!silent) + vc_container_helper_format_debug(ctx, indent, "%s: \"%ls\"", name, buffer); + return value == (uint64_t)size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; + } + + if(type == LOG_FORMAT_TYPE_UINT_LE) + { + switch(size) + { + case 1: status = vc_container_io_write_uint8(ctx->priv->io, (uint8_t)value); break; + case 2: status = vc_container_io_write_le_uint16(ctx->priv->io, (uint16_t)value); break; + case 3: status = vc_container_io_write_le_uint24(ctx->priv->io, (uint32_t)value); break; + case 4: status = vc_container_io_write_le_uint32(ctx->priv->io, (uint32_t)value); break; + case 8: status = vc_container_io_write_le_uint64(ctx->priv->io, value); break; + } + } + else if(type == LOG_FORMAT_TYPE_UINT_BE) + { + switch(size) + { + case 1: status = vc_container_io_write_uint8(ctx->priv->io, (uint8_t)value); break; + case 2: status = vc_container_io_write_be_uint16(ctx->priv->io, (uint16_t)value); break; + case 3: status = vc_container_io_write_be_uint24(ctx->priv->io, (uint32_t)value); break; + case 4: status = vc_container_io_write_be_uint32(ctx->priv->io, (uint32_t)value); break; + case 8: status = vc_container_io_write_be_uint64(ctx->priv->io, value); break; + } + } + else if(type == LOG_FORMAT_TYPE_FOURCC) + { + status = vc_container_io_write_fourcc(ctx->priv->io, (uint32_t)value); + } + else if(type == LOG_FORMAT_TYPE_GUID) + { + value = vc_container_io_write(ctx->priv->io, buffer, 16); + } + else + { + vc_container_assert(0); + return 0; + } + + if(status) + { + vc_container_helper_format_debug(ctx, indent, "write failed for %s", name); + return status; + } + + if(!silent) + { + if (type == LOG_FORMAT_TYPE_FOURCC) + { + vc_container_helper_format_debug(ctx, indent, "%s: %4.4s", name, (char *)&value); + } + else if(type == LOG_FORMAT_TYPE_GUID) + { + GUID_T guid; + memcpy(&guid, buffer, sizeof(guid)); + vc_container_helper_format_debug(ctx, indent, "%s: 0x%x-0x%x-0x%x-0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", + name, guid.word0, guid.short0, guid.short1, + guid.bytes[0], guid.bytes[1], guid.bytes[2], guid.bytes[3], + guid.bytes[4], guid.bytes[5], guid.bytes[6], guid.bytes[7]); + } + else + { + vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value); + } + } + + return status; +} diff --git a/containers/core/containers_io_helpers.h b/containers/core/containers_io_helpers.h new file mode 100755 index 0000000..1f9b460 --- /dev/null +++ b/containers/core/containers_io_helpers.h @@ -0,0 +1,716 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_IO_HELPERS_H +#define VC_CONTAINERS_IO_HELPERS_H + +/** \file containers_io_helpers.h + * Helper functions and macros which provide functionality which is often used by containers + */ + +#include "containers/core/containers_io.h" +#include "containers/core/containers_utils.h" + +/***************************************************************************** + * Helper inline functions to read integers from an i/o stream + *****************************************************************************/ + +/** Reads an unsigned 8 bits integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint8_t vc_container_io_read_uint8(VC_CONTAINER_IO_T *io) +{ + uint8_t value; + size_t ret = vc_container_io_read(io, &value, 1); + return ret == 1 ? value : 0; +} + +/** Reads a FOURCC from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The FOURCC to read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE VC_CONTAINER_FOURCC_T vc_container_io_read_fourcc(VC_CONTAINER_IO_T *io) +{ + VC_CONTAINER_FOURCC_T value; + size_t ret = vc_container_io_read(io, (int8_t *)&value, 4); + return ret == 4 ? value : 0; +} + +/** Reads an unsigned 16 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint16_t vc_container_io_read_be_uint16(VC_CONTAINER_IO_T *io) +{ + uint8_t value[2]; + size_t ret = vc_container_io_read(io, value, 2); + return ret == 2 ? (value[0] << 8) | value[1] : 0; +} + +/** Reads an unsigned 24 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_read_be_uint24(VC_CONTAINER_IO_T *io) +{ + uint8_t value[3]; + size_t ret = vc_container_io_read(io, value, 3); + return ret == 3 ? (value[0] << 16) | (value[1] << 8) | value[2] : 0; +} + +/** Reads an unsigned 32 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_read_be_uint32(VC_CONTAINER_IO_T *io) +{ + uint8_t value[4]; + size_t ret = vc_container_io_read(io, value, 4); + return ret == 4 ? (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3] : 0; +} + +/** Reads an unsigned 40 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_be_uint40(VC_CONTAINER_IO_T *io) +{ + uint8_t value[5]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 5); + + value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; + value2 = value[4]; + + return ret == 5 ? (((uint64_t)value1) << 8)|value2 : 0; +} + +/** Reads an unsigned 48 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_be_uint48(VC_CONTAINER_IO_T *io) +{ + uint8_t value[6]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 6); + + value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; + value2 = (value[4] << 8) | value[5]; + + return ret == 6 ? (((uint64_t)value1) << 16)|value2 : 0; +} + +/** Reads an unsigned 56 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_be_uint56(VC_CONTAINER_IO_T *io) +{ + uint8_t value[7]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 7); + + value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; + value2 = (value[4] << 16) | (value[5] << 8) | value[6]; + + return ret == 7 ? (((uint64_t)value1) << 24)|value2 : 0; +} + +/** Reads an unsigned 64 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_be_uint64(VC_CONTAINER_IO_T *io) +{ + uint8_t value[8]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 8); + + value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; + value2 = (value[4] << 24) | (value[5] << 16) | (value[6] << 8) | value[7]; + + return ret == 8 ? (((uint64_t)value1) << 32)|value2 : 0; +} + +/** Reads an unsigned 16 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint16_t vc_container_io_read_le_uint16(VC_CONTAINER_IO_T *io) +{ + uint8_t value[2]; + size_t ret = vc_container_io_read(io, value, 2); + return ret == 2 ? (value[1] << 8) | value[0] : 0; +} + +/** Reads an unsigned 24 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_read_le_uint24(VC_CONTAINER_IO_T *io) +{ + uint8_t value[3]; + size_t ret = vc_container_io_read(io, value, 3); + return ret == 3 ? (value[2] << 16) | (value[1] << 8) | value[0] : 0; +} + +/** Reads an unsigned 32 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_read_le_uint32(VC_CONTAINER_IO_T *io) +{ + uint8_t value[4]; + size_t ret = vc_container_io_read(io, value, 4); + return ret == 4 ? (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0] : 0; +} + +/** Reads an unsigned 40 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_le_uint40(VC_CONTAINER_IO_T *io) +{ + uint8_t value[5]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 5); + + value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; + value2 = value[4]; + + return ret == 5 ? (((uint64_t)value2) << 32)|value1 : 0; +} + +/** Reads an unsigned 48 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_le_uint48(VC_CONTAINER_IO_T *io) +{ + uint8_t value[6]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 6); + + value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; + value2 = (value[5] << 8) | value[4]; + + return ret == 6 ? (((uint64_t)value2) << 32)|value1 : 0; +} + +/** Reads an unsigned 56 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_le_uint56(VC_CONTAINER_IO_T *io) +{ + uint8_t value[7]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 7); + + value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; + value2 = (value[6] << 16) | (value[5] << 8) | value[4]; + + return ret == 7 ? (((uint64_t)value2) << 32)|value1 : 0; +} + +/** Reads an unsigned 64 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_read_le_uint64(VC_CONTAINER_IO_T *io) +{ + uint8_t value[8]; + uint32_t value1, value2; + size_t ret = vc_container_io_read(io, value, 8); + + value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; + value2 = (value[7] << 24) | (value[6] << 16) | (value[5] << 8) | value[4]; + + return ret == 8 ? (((uint64_t)value2) << 32)|value1 : 0; +} + +/***************************************************************************** + * Helper inline functions to peek integers from an i/o stream + *****************************************************************************/ + +/** Peeks an unsigned 8 bits integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint8_t vc_container_io_peek_uint8(VC_CONTAINER_IO_T *io) +{ + uint8_t value; + size_t ret = vc_container_io_peek(io, &value, 1); + return ret == 1 ? value : 0; +} + +/** Peeks an unsigned 16 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint16_t vc_container_io_peek_be_uint16(VC_CONTAINER_IO_T *io) +{ + uint8_t value[2]; + size_t ret = vc_container_io_peek(io, value, 2); + return ret == 2 ? (value[0] << 8) | value[1] : 0; +} + +/** Peeks an unsigned 24 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_peek_be_uint24(VC_CONTAINER_IO_T *io) +{ + uint8_t value[3]; + size_t ret = vc_container_io_peek(io, value, 3); + return ret == 3 ? (value[0] << 16) | (value[1] << 8) | value[2] : 0; +} + +/** Peeks an unsigned 32 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_peek_be_uint32(VC_CONTAINER_IO_T *io) +{ + uint8_t value[4]; + size_t ret = vc_container_io_peek(io, value, 4); + return ret == 4 ? (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3] : 0; +} + +/** Peeks an unsigned 64 bits big endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_peek_be_uint64(VC_CONTAINER_IO_T *io) +{ + uint8_t value[8]; + uint32_t value1, value2; + size_t ret = vc_container_io_peek(io, value, 8); + + value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]; + value2 = (value[4] << 24) | (value[5] << 16) | (value[6] << 8) | value[7]; + + return ret == 8 ? (((uint64_t)value1) << 32)|value2 : 0; +} + +/** Peeks an unsigned 16 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint16_t vc_container_io_peek_le_uint16(VC_CONTAINER_IO_T *io) +{ + uint8_t value[2]; + size_t ret = vc_container_io_peek(io, value, 2); + return ret == 2 ? (value[1] << 8) | value[0] : 0; +} + +/** Peeks an unsigned 24 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_peek_le_uint24(VC_CONTAINER_IO_T *io) +{ + uint8_t value[3]; + size_t ret = vc_container_io_peek(io, value, 3); + return ret == 3 ? (value[2] << 16) | (value[1] << 8) | value[0] : 0; +} + +/** Peeks an unsigned 32 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint32_t vc_container_io_peek_le_uint32(VC_CONTAINER_IO_T *io) +{ + uint8_t value[4]; + size_t ret = vc_container_io_peek(io, value, 4); + return ret == 4 ? (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0] : 0; +} + +/** Peeks an unsigned 64 bits little endian integer from an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \return The integer read. In case of failure during the read, + * this will return a value of 0. + */ +STATIC_INLINE uint64_t vc_container_io_peek_le_uint64(VC_CONTAINER_IO_T *io) +{ + uint8_t value[8]; + uint32_t value1, value2; + size_t ret = vc_container_io_peek(io, value, 8); + + value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0]; + value2 = (value[7] << 24) | (value[6] << 16) | (value[5] << 8) | value[4]; + + return ret == 8 ? (((uint64_t)value2) << 32)|value1 : 0; +} + +/***************************************************************************** + * Helper inline functions to write integers to an i/o stream + *****************************************************************************/ + +/** Writes an unsigned 8 bits integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_uint8(VC_CONTAINER_IO_T *io, uint8_t value) +{ + size_t ret = vc_container_io_write(io, &value, 1); + return ret == 1 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes a FOURCC to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The FOURCC to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_fourcc(VC_CONTAINER_IO_T *io, VC_CONTAINER_FOURCC_T value) +{ + size_t ret = vc_container_io_write(io, (uint8_t *)&value, 4); + return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 16 bits big endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint16(VC_CONTAINER_IO_T *io, uint16_t value) +{ + uint8_t bytes[2] = {(uint8_t)(value >> 8), (uint8_t)value}; + size_t ret = vc_container_io_write(io, bytes, 2); + return ret == 2 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 24 bits big endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint24(VC_CONTAINER_IO_T *io, uint32_t value) +{ + uint8_t bytes[3] = {(uint8_t)(value >> 16), (uint8_t)(value >> 8), (uint8_t)value}; + size_t ret = vc_container_io_write(io, bytes, 3); + return ret == 3 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 32 bits big endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint32(VC_CONTAINER_IO_T *io, uint32_t value) +{ + uint8_t bytes[4] = {value >> 24, value >> 16, value >> 8, value}; + size_t ret = vc_container_io_write(io, bytes, 4); + return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 64 bits big endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint64(VC_CONTAINER_IO_T *io, uint64_t value) +{ + uint8_t bytes[8] = + { + (uint8_t)(value >> 56), + (uint8_t)(value >> 48), + (uint8_t)(value >> 40), + (uint8_t)(value >> 32), + (uint8_t)(value >> 24), + (uint8_t)(value >> 16), + (uint8_t)(value >> 8), + (uint8_t) value + }; + size_t ret = vc_container_io_write(io, bytes, 8); + return ret == 8 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 16 bits little endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint16(VC_CONTAINER_IO_T *io, uint16_t value) +{ + uint8_t bytes[2] = {(uint8_t)value, (uint8_t)(value >> 8)}; + size_t ret = vc_container_io_write(io, bytes, 2); + return ret == 2 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 24 bits little endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint24(VC_CONTAINER_IO_T *io, uint32_t value) +{ + uint8_t bytes[3] = {value, value >> 8, value >> 16}; + size_t ret = vc_container_io_write(io, bytes, 3); + return ret == 3 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 32 bits little endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint32(VC_CONTAINER_IO_T *io, uint32_t value) +{ + uint8_t bytes[4] = {value, value >> 8, value >> 16, value >> 24}; + size_t ret = vc_container_io_write(io, bytes, 4); + return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/** Writes an unsigned 64 bits little endian integer to an i/o stream. + * \param io Pointer to the VC_CONTAINER_IO_T instance to use + * \param value The integer to write. + * \return The status of the operation. + */ +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint64(VC_CONTAINER_IO_T *io, uint64_t value) +{ + uint8_t bytes[8] = + { + (uint8_t) value, + (uint8_t)(value >> 8), + (uint8_t)(value >> 16), + (uint8_t)(value >> 24), + (uint8_t)(value >> 32), + (uint8_t)(value >> 40), + (uint8_t)(value >> 48), + (uint8_t)(value >> 56) + }; + size_t ret = vc_container_io_write(io, bytes, 8); + return ret == 8 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED; +} + +/***************************************************************************** + * Helper macros for accessing the i/o stream. These will also call the right + * functions depending on the endianness defined. + *****************************************************************************/ + +/** Macro which returns the current position within the stream */ +#define STREAM_POSITION(ctx) (ctx)->priv->io->offset +/** Macro which returns true if the end of stream has been reached */ +#define STREAM_EOS(ctx) ((ctx)->priv->io->status == VC_CONTAINER_ERROR_EOS) +/** Macro which returns the status of the stream */ +#define STREAM_STATUS(ctx) (ctx)->priv->io->status +/** Macro which returns true if an error other than end of stream has occurred */ +#define STREAM_ERROR(ctx) ((ctx)->priv->io->status && (ctx)->priv->io->status != VC_CONTAINER_ERROR_EOS) +/** Macro which returns true if we can seek into the stream */ +#define STREAM_SEEKABLE(ctx) (!((ctx)->priv->io->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK)) + +#define PEEK_BYTES(ctx, buffer, size) vc_container_io_peek((ctx)->priv->io, buffer, (size_t)(size)) +#define READ_BYTES(ctx, buffer, size) vc_container_io_read((ctx)->priv->io, buffer, (size_t)(size)) +#define SKIP_BYTES(ctx, size) vc_container_io_skip((ctx)->priv->io, (size_t)(size)) +#define SEEK(ctx, off) vc_container_io_seek((ctx)->priv->io, (int64_t)(off)) +#define CACHE_BYTES(ctx, size) vc_container_io_cache((ctx)->priv->io, (size_t)(size)) + +#define _SKIP_GUID(ctx) vc_container_io_skip((ctx)->priv->io, 16) +#define _SKIP_U8(ctx) (vc_container_io_skip((ctx)->priv->io, 1) != 1) +#define _SKIP_U16(ctx) (vc_container_io_skip((ctx)->priv->io, 2) != 2) +#define _SKIP_U24(ctx) (vc_container_io_skip((ctx)->priv->io, 3) != 3) +#define _SKIP_U32(ctx) (vc_container_io_skip((ctx)->priv->io, 4) != 4) +#define _SKIP_U64(ctx) (vc_container_io_skip((ctx)->priv->io, 8) != 8) +#define _SKIP_FOURCC(ctx) (vc_container_io_skip((ctx)->priv->io, 4) != 4) + +#define _READ_GUID(ctx, buffer) vc_container_io_read((ctx)->priv->io, buffer, 16) +#define _READ_U8(ctx) vc_container_io_read_uint8((ctx)->priv->io) +#define _READ_FOURCC(ctx) vc_container_io_read_fourcc((ctx)->priv->io) +#define PEEK_GUID(ctx, buffer) vc_container_io_peek((ctx)->priv->io, buffer, 16) +#define PEEK_U8(ctx) vc_container_io_peek_uint8((ctx)->priv->io) +#ifdef CONTAINER_IS_BIG_ENDIAN +# define _READ_U16(ctx) vc_container_io_read_be_uint16((ctx)->priv->io) +# define _READ_U24(ctx) vc_container_io_read_be_uint24((ctx)->priv->io) +# define _READ_U32(ctx) vc_container_io_read_be_uint32((ctx)->priv->io) +# define _READ_U40(ctx) vc_container_io_read_be_uint40((ctx)->priv->io) +# define _READ_U48(ctx) vc_container_io_read_be_uint48((ctx)->priv->io) +# define _READ_U56(ctx) vc_container_io_read_be_uint56((ctx)->priv->io) +# define _READ_U64(ctx) vc_container_io_read_be_uint64((ctx)->priv->io) +# define PEEK_U16(ctx) vc_container_io_peek_be_uint16((ctx)->priv->io) +# define PEEK_U24(ctx) vc_container_io_peek_be_uint24((ctx)->priv->io) +# define PEEK_U32(ctx) vc_container_io_peek_be_uint32((ctx)->priv->io) +# define PEEK_U64(ctx) vc_container_io_peek_be_uint64((ctx)->priv->io) +#else +# define _READ_U16(ctx) vc_container_io_read_le_uint16((ctx)->priv->io) +# define _READ_U24(ctx) vc_container_io_read_le_uint24((ctx)->priv->io) +# define _READ_U32(ctx) vc_container_io_read_le_uint32((ctx)->priv->io) +# define _READ_U40(ctx) vc_container_io_read_le_uint40((ctx)->priv->io) +# define _READ_U48(ctx) vc_container_io_read_le_uint48((ctx)->priv->io) +# define _READ_U56(ctx) vc_container_io_read_le_uint56((ctx)->priv->io) +# define _READ_U64(ctx) vc_container_io_read_le_uint64((ctx)->priv->io) +# define PEEK_U16(ctx) vc_container_io_peek_le_uint16((ctx)->priv->io) +# define PEEK_U24(ctx) vc_container_io_peek_le_uint24((ctx)->priv->io) +# define PEEK_U32(ctx) vc_container_io_peek_le_uint32((ctx)->priv->io) +# define PEEK_U64(ctx) vc_container_io_peek_le_uint64((ctx)->priv->io) +#endif + +#define WRITE_BYTES(ctx, buffer, size) vc_container_io_write((ctx)->priv->io, buffer, (size_t)(size)) +#define _WRITE_GUID(ctx, buffer) vc_container_io_write((ctx)->priv->io, buffer, 16) +#define _WRITE_U8(ctx, v) vc_container_io_write_uint8((ctx)->priv->io, v) +#define _WRITE_FOURCC(ctx, v) vc_container_io_write_fourcc((ctx)->priv->io, v) +#ifdef CONTAINER_IS_BIG_ENDIAN +# define _WRITE_U16(ctx, v) vc_container_io_write_be_uint16((ctx)->priv->io, v) +# define _WRITE_U24(ctx, v) vc_container_io_write_be_uint24((ctx)->priv->io, v) +# define _WRITE_U32(ctx, v) vc_container_io_write_be_uint32((ctx)->priv->io, v) +# define _WRITE_U64(ctx, v) vc_container_io_write_be_uint64((ctx)->priv->io, v) +#else +# define _WRITE_U16(ctx, v) vc_container_io_write_le_uint16((ctx)->priv->io, v) +# define _WRITE_U24(ctx, v) vc_container_io_write_le_uint24((ctx)->priv->io, v) +# define _WRITE_U32(ctx, v) vc_container_io_write_le_uint32((ctx)->priv->io, v) +# define _WRITE_U64(ctx, v) vc_container_io_write_le_uint64((ctx)->priv->io, v) +#endif + +#ifndef CONTAINER_HELPER_LOG_INDENT +# define CONTAINER_HELPER_LOG_INDENT(a) 0 +#endif + +#ifdef CONTAINER_IS_BIG_ENDIAN +# define LOG_FORMAT_TYPE_UINT LOG_FORMAT_TYPE_UINT_BE +# define LOG_FORMAT_TYPE_STRING_UTF16 LOG_FORMAT_TYPE_STRING_UTF16_BE +#else +# define LOG_FORMAT_TYPE_UINT LOG_FORMAT_TYPE_UINT_LE +# define LOG_FORMAT_TYPE_STRING_UTF16 LOG_FORMAT_TYPE_STRING_UTF16_LE +#endif + +#ifndef ENABLE_CONTAINERS_LOG_FORMAT +#define SKIP_GUID(ctx,n) _SKIP_GUID(ctx) +#define SKIP_U8(ctx,n) _SKIP_U8(ctx) +#define SKIP_U16(ctx,n) _SKIP_U16(ctx) +#define SKIP_U24(ctx,n) _SKIP_U24(ctx) +#define SKIP_U32(ctx,n) _SKIP_U32(ctx) +#define SKIP_U64(ctx,n) _SKIP_U64(ctx) +#define SKIP_FOURCC(ctx,n) _SKIP_FOURCC(ctx) +#define READ_GUID(ctx,buffer,n) _READ_GUID(ctx,(uint8_t *)buffer) +#define READ_U8(ctx,n) _READ_U8(ctx) +#define READ_U16(ctx,n) _READ_U16(ctx) +#define READ_U24(ctx,n) _READ_U24(ctx) +#define READ_U32(ctx,n) _READ_U32(ctx) +#define READ_U40(ctx,n) _READ_U40(ctx) +#define READ_U48(ctx,n) _READ_U48(ctx) +#define READ_U56(ctx,n) _READ_U56(ctx) +#define READ_U64(ctx,n) _READ_U64(ctx) +#define READ_FOURCC(ctx,n) _READ_FOURCC(ctx) +#define READ_STRING(ctx,buffer,sz,n) READ_BYTES(ctx,buffer,sz) +#define READ_STRING_UTF16(ctx,buffer,sz,n) READ_BYTES(ctx,buffer,sz) +#define SKIP_STRING(ctx,sz,n) SKIP_BYTES(ctx,sz) +#define SKIP_STRING_UTF16(ctx,sz,n) SKIP_BYTES(ctx,sz) +#else +#define SKIP_GUID(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_U8(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_U16(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_U24(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_U32(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_U64(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_FOURCC(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define READ_GUID(ctx,buffer,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U8(ctx,n) (uint8_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U16(ctx,n) (uint16_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U24(ctx,n) (uint32_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U32(ctx,n) (uint32_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U40(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 5, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U48(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 6, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U56(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 7, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_U64(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_FOURCC(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_STRING_UTF16(ctx,buffer,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING_UTF16, sz, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define READ_STRING(ctx,buffer,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING, sz, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0) +#define SKIP_STRING_UTF16(ctx,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING_UTF16, sz, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#define SKIP_STRING(ctx,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING, sz, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1) +#endif + +#ifndef ENABLE_CONTAINERS_LOG_FORMAT +#define WRITE_GUID(ctx,buffer,n) _WRITE_GUID(ctx,(const uint8_t *)buffer) +#define WRITE_U8(ctx,v,n) _WRITE_U8(ctx,(uint8_t)(v)) +#define WRITE_FOURCC(ctx,v,n) _WRITE_FOURCC(ctx,(uint32_t)(v)) +#define WRITE_U16(ctx,v,n) _WRITE_U16(ctx,(uint16_t)(v)) +#define WRITE_U24(ctx,v,n) _WRITE_U24(ctx,(uint32_t)(v)) +#define WRITE_U32(ctx,v,n) _WRITE_U32(ctx,(uint32_t)(v)) +#define WRITE_U64(ctx,v,n) _WRITE_U64(ctx,(uint64_t)(v)) +#define WRITE_STRING(ctx,buffer,size,n) WRITE_BYTES(ctx, buffer, size) +#else +#define WRITE_GUID(ctx,buffer,n) (vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, UINT64_C(0), (const uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) ? 0 : 16) +#define WRITE_U8(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_FOURCC(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_U16(ctx,v,n) (uint16_t)vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_U24(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_U32(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_U64(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) +#define WRITE_STRING(ctx,buffer,size,n) (vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_STRING, size, n, UINT64_C(0), (const uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) ? 0 : size) +#endif + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT +#define LOG_FORMAT(ctx, ...) do { if((ctx)->priv->io->module) vc_container_helper_format_debug(ctx, CONTAINER_HELPER_LOG_INDENT(ctx), __VA_ARGS__); } while(0) +#else +#define LOG_FORMAT(ctx, ...) do {} while (0) +#endif + +#define LOG_FORMAT_TYPE_UINT_LE 0 +#define LOG_FORMAT_TYPE_UINT_BE 1 +#define LOG_FORMAT_TYPE_STRING 2 +#define LOG_FORMAT_TYPE_STRING_UTF16_LE 3 +#define LOG_FORMAT_TYPE_STRING_UTF16_BE 4 +#define LOG_FORMAT_TYPE_FOURCC 5 +#define LOG_FORMAT_TYPE_GUID 6 +#define LOG_FORMAT_TYPE_HEX 0x100 + +uint64_t vc_container_helper_int_debug(VC_CONTAINER_T *ctx, int type, uint64_t value, const char *name, int indent); +uint64_t vc_container_helper_read_debug(VC_CONTAINER_T *ctx, int type, int size, const char *name, + uint8_t *buffer, int indent, int b_skip); +VC_CONTAINER_STATUS_T vc_container_helper_write_debug(VC_CONTAINER_T *ctx, int type, int size, const char *name, + uint64_t value, const uint8_t *buffer, int indent, int silent); +void vc_container_helper_format_debug(VC_CONTAINER_T *ctx, int indent, const char *format, ...); + +#endif /* VC_CONTAINERS_IO_HELPERS_H */ +/* End of file */ +/*-----------------------------------------------------------------------------*/ diff --git a/containers/core/containers_list.c b/containers/core/containers_list.c new file mode 100755 index 0000000..333f052 --- /dev/null +++ b/containers/core/containers_list.c @@ -0,0 +1,221 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#include "containers/core/containers_common.h" +#include "containers/core/containers_list.h" + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/****************************************************************************** +Function prototypes +******************************************************************************/ + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/** Find an entry in the list, or the insertion point. + * Uses binary sub-division to find the search item. If index is not NULL, the + * index of the matching entry, or the point at which to insert if not found, is + * written to that address. + * + * \param list The list to be searched. + * \param entry The entry for which to search. + * \param index Set to index of match, or insertion point if not found. May be NULL. + * \return True if a match was found, false if not. */ +static bool vc_containers_list_find_index(const VC_CONTAINERS_LIST_T *list, + const void *entry, + uint32_t *index) +{ + const char *entries = (const char *)list->entries; + size_t entry_size = list->entry_size; + VC_CONTAINERS_LIST_COMPARATOR_T comparator = list->comparator; + uint32_t start = 0, end = list->size; + uint32_t mid = end >> 1; + bool match = false; + + while (mid < end) + { + int comparison = comparator(entry, entries + mid * entry_size); + + if (comparison < 0) + end = mid; + else if (comparison > 0) + start = mid + 1; + else { + match = true; + break; + } + + mid = (start + end) >> 1; + } + + if (index) *index = mid; + return match; +} + +/****************************************************************************** +Functions exported as part of the API +******************************************************************************/ + +/*****************************************************************************/ +VC_CONTAINERS_LIST_T *vc_containers_list_create(uint32_t capacity, + size_t entry_size, + VC_CONTAINERS_LIST_COMPARATOR_T comparator) +{ + VC_CONTAINERS_LIST_T *list; + + list = (VC_CONTAINERS_LIST_T *)malloc(sizeof(VC_CONTAINERS_LIST_T)); + if (!list) + return NULL; + + /* Ensure non-zero capacity, as that signifies a read-only list */ + if (!capacity) capacity = 1; + + list->entries = malloc(capacity * entry_size); + if (!list->entries) + { + free(list); + return NULL; + } + + list->size = 0; + list->capacity = capacity; + list->entry_size = entry_size; + list->comparator = comparator; + + return list; +} + +/*****************************************************************************/ +void vc_containers_list_destroy(VC_CONTAINERS_LIST_T *list) +{ + /* Avoid trying to destroy read-only lists */ + if (list && list->capacity) + { + if (list->entries) + free(list->entries); + free(list); + } +} + +/*****************************************************************************/ +void vc_containers_list_reset(VC_CONTAINERS_LIST_T *list) +{ + /* Avoid trying to reset read-only lists */ + if (list && list->capacity) + list->size = 0; +} + +/*****************************************************************************/ +bool vc_containers_list_insert(VC_CONTAINERS_LIST_T *list, + void *new_entry, + bool allow_duplicates) +{ + uint32_t insert_idx; + char *insert_ptr; + size_t entry_size; + bool match; + + if (!list || !list->capacity) return false; + + entry_size = list->entry_size; + match = vc_containers_list_find_index(list, new_entry, &insert_idx); + insert_ptr = (char *)list->entries + entry_size * insert_idx; + + if (!match || allow_duplicates) + { + /* Ensure there is space for the new entry */ + if (list->size == list->capacity) + { + void *new_entries = realloc(list->entries, (list->size + 1) * entry_size); + + if (!new_entries) + return false; + list->entries = new_entries; + list->capacity++; + } + + /* Move up anything above the insertion point */ + if (insert_idx < list->size) + memmove(insert_ptr + entry_size, insert_ptr, (list->size - insert_idx) * entry_size); + + list->size++; + } + + /* Copy in the new entry (overwriting the old one if necessary) */ + memcpy(insert_ptr, new_entry, list->entry_size); + + return true; +} + +/*****************************************************************************/ +bool vc_containers_list_find_entry(const VC_CONTAINERS_LIST_T *list, + void *entry) +{ + uint32_t index; + size_t entry_size; + + if (!vc_containers_list_find_index(list, entry, &index)) + return false; + + entry_size = list->entry_size; + memcpy(entry, (const char *)list->entries + entry_size * index, entry_size); + + return true; +} + +/*****************************************************************************/ +void vc_containers_list_validate(const VC_CONTAINERS_LIST_T *list) +{ + uint32_t ii, entry_size; + const uint8_t *entry_ptr; + + vc_container_assert(list); + vc_container_assert(!list->capacity || list->size <= list->capacity); + vc_container_assert(list->entry_size); + vc_container_assert(list->comparator); + vc_container_assert(list->entries); + + /* Check all entries are in sorted order */ + entry_ptr = (const uint8_t *)list->entries; + entry_size = list->entry_size; + for (ii = 1; ii < list->size; ii++) + { + vc_container_assert(list->comparator(entry_ptr, entry_ptr + entry_size) <= 0); + entry_ptr += entry_size; + } +} diff --git a/containers/core/containers_list.h b/containers/core/containers_list.h new file mode 100755 index 0000000..5b08997 --- /dev/null +++ b/containers/core/containers_list.h @@ -0,0 +1,102 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VC_CONTAINERS_LIST_H_ +#define _VC_CONTAINERS_LIST_H_ + +#include "containers/containers.h" + +/** List entry comparison prototype. + * Returns zero if items at a and b match, positive if a is "bigger" than b and + * negative if a is "smaller" than b. */ +typedef int (*VC_CONTAINERS_LIST_COMPARATOR_T)(const void *a, const void *b); + +/** Sorted list type. + * Storage type providing efficient insertion and search via binary sub-division. */ +typedef struct vc_containers_list_tag +{ + uint32_t size; /**< Number of defined entries in list */ + uint32_t capacity; /**< Capacity of list, in entries, or zero for read-only */ + size_t entry_size; /**< Size of one entry, in bytes */ + VC_CONTAINERS_LIST_COMPARATOR_T comparator; /**< Entry comparison function */ + void *entries; /**< Pointer to array of entries */ +} VC_CONTAINERS_LIST_T; + +/** Macro to generate a static, read-only list from an array and comparator */ +#define VC_CONTAINERS_STATIC_LIST(L, A, C) static VC_CONTAINERS_LIST_T L = { countof(A), 0, sizeof(*(A)), (VC_CONTAINERS_LIST_COMPARATOR_T)(C), A } + + +/** Create an empty list. + * The list is created based on the details provided, minimum capacity one entry. + * + * \param The initial capacity in entries. + * \param entry_size The size of each entry, in bytes. + * \param comparator A function for comparing two entries. + * \return The new list or NULL. */ +VC_CONTAINERS_LIST_T *vc_containers_list_create(uint32_t capacity, size_t entry_size, VC_CONTAINERS_LIST_COMPARATOR_T comparator); + +/** Destroy a list. + * Has no effect on a static list. + * + * \param list The list to be destroyed. */ +void vc_containers_list_destroy(VC_CONTAINERS_LIST_T *list); + +/** Reset a list to be empty. + * Has no effect on a static list. + * + * \param list The list to be reset. */ +void vc_containers_list_reset(VC_CONTAINERS_LIST_T *list); + +/** Insert an entry into the list. + * + * \param list The list. + * \param new_entry The new entry to be inserted. + * \param allow_duplicates Determines whether to insert or overwrite if there + * is an existing matching entry. + * \return True if the entry has successfully been inserted, false if the list + * needed to be enlarged and the memory allocation failed. */ +bool vc_containers_list_insert(VC_CONTAINERS_LIST_T *list, void *new_entry, bool allow_duplicates); + +/** Find an entry in the list and fill in the result. + * Searches for an entry in the list using the comparator and if found + * overwrites the one passed in with the one found. + * + * \param list The list to search. + * \param entry An entry with enough defined to find it in the list, filled in + * with the rest if found. + * \return True if found, false if not. */ +bool vc_containers_list_find_entry(const VC_CONTAINERS_LIST_T *list, void *entry); + +/** Validates a list pointer. + * Fields and contents of a list are checked and asserted to be correct. With a + * large list this may be slow, so it is recommended only to call this in debug + * builds. + * + * \param list The list to be validated. */ +void vc_containers_list_validate(const VC_CONTAINERS_LIST_T *list); + +#endif /* _VC_CONTAINERS_LIST_H_ */ diff --git a/containers/core/containers_loader.c b/containers/core/containers_loader.c new file mode 100755 index 0000000..b597301 --- /dev/null +++ b/containers/core/containers_loader.c @@ -0,0 +1,436 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_loader.h" + +#if !defined(ENABLE_CONTAINERS_STANDALONE) + #include "vcos_dlfcn.h" + #define DL_SUFFIX VCOS_SO_EXT + #ifndef DL_PATH_PREFIX + #define DL_PATH_PREFIX "" + #endif +#endif + +/****************************************************************************** +Type definitions. +******************************************************************************/ + +typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_READER_OPEN_FUNC_T)(VC_CONTAINER_T *); +typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_WRITER_OPEN_FUNC_T)(VC_CONTAINER_T *); + +/****************************************************************************** +Prototypes for local functions +******************************************************************************/ + +static void reset_context(VC_CONTAINER_T *p_ctx); +static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read); +static void unload_library(void *handle); +static VC_CONTAINER_READER_OPEN_FUNC_T load_reader(void **handle, const char *name); +static VC_CONTAINER_READER_OPEN_FUNC_T load_writer(void **handle, const char *name); +static VC_CONTAINER_READER_OPEN_FUNC_T load_metadata_reader(void **handle, const char *name); +static const char* container_for_fileext(const char *fileext); + +/******************************************************************************** + List of supported containers + ********************************************************************************/ + +static const char *readers[] = +{"mp4", "asf", "avi", "mkv", "wav", "flv", "simple", "rawvideo", "mpga", "ps", "rtp", "rtsp", "rcv", "rv9", "qsynth", "binary", 0}; +static const char *writers[] = +{"mp4", "asf", "avi", "binary", "simple", "rawvideo", 0}; +static const char *metadata_readers[] = +{"id3", 0}; + +#if defined(ENABLE_CONTAINERS_STANDALONE) +VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T mkv_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * ); +VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * ); + +VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T * ); + +static struct +{ + const char *name; + VC_CONTAINER_READER_OPEN_FUNC_T func; +} reader_entry_points[] = +{ + {"asf", &asf_reader_open}, + {"avi", &avi_reader_open}, + {"mpga", &mpga_reader_open}, + {"mkv", &mkv_reader_open}, + {"wav", &wav_reader_open}, + {"mp4", &mp4_reader_open}, + {"flv", &flv_reader_open}, + {"ps", &ps_reader_open}, + {"binary", &binary_reader_open}, + {"rtp", &rtp_reader_open}, + {"rtsp", &rtsp_reader_open}, + {"rcv", &rcv_reader_open}, + {"rv9", &rv9_reader_open}, + {"qsynth", &qsynth_reader_open}, + {"simple", &simple_reader_open}, + {"rawvideo", &rawvideo_reader_open}, + {0, 0} +}; + +static struct +{ + const char *name; + VC_CONTAINER_READER_OPEN_FUNC_T func; +} metadata_reader_entry_points[] = +{ + {"id3", &id3_metadata_reader_open}, + {0, 0} +}; + +static struct +{ + const char *name; + VC_CONTAINER_WRITER_OPEN_FUNC_T func; +} writer_entry_points[] = +{ + {"avi", &avi_writer_open}, + {"mp4", &mp4_writer_open}, + {"binary", &binary_writer_open}, + {"simple", &simple_writer_open}, + {"rawvideo", &rawvideo_writer_open}, + {0, 0} +}; +#endif /* defined(ENABLE_CONTAINERS_STANDALONE) */ + +/** Table describing the mapping between file extensions and container name. + This is only used as optimisation to decide which container to try first. + Entries where the file extension and container have the same name can be omitted. */ +static const struct { + const char *extension; + const char *container; +} extension_container_mapping[] = +{ + { "wma", "asf" }, + { "wmv", "asf" }, + { "mov", "mp4" }, + { "3gp", "mp4" }, + { "mp2", "mpga" }, + { "mp3", "mpga" }, + { "webm", "mkv" }, + { "mid", "qsynth" }, + { "mld", "qsynth" }, + { "mmf", "qsynth" }, + { 0, 0 } +}; + +/******************************************************************************** + Public functions + ********************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_load_reader(VC_CONTAINER_T *p_ctx, const char *fileext) +{ + const char *name; + void *handle = NULL; + VC_CONTAINER_READER_OPEN_FUNC_T func; + VC_CONTAINER_STATUS_T status; + unsigned int i; + int64_t offset; + + vc_container_assert(p_ctx && !p_ctx->priv->module_handle); + + /* FIXME: the missing part here is code that reads a configuration or + searches the filesystem for container libraries. Instead, we currently + rely on static arrays i.e. 'readers', 'writers', etc. */ + + /* Before trying proper container readers, iterate through metadata + readers to parse tags concatenated to start/end of stream */ + for(i = 0; metadata_readers[i]; i++) + { + if ((func = load_metadata_reader(&handle, metadata_readers[i])) != NULL) + { + status = (*func)(p_ctx); + if(!status && p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx); + reset_context(p_ctx); + unload_library(handle); + if(status == VC_CONTAINER_SUCCESS) break; + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + } + } + + /* Store the current position, in case any containers don't leave the stream + at the start, and the IO layer can cope with the seek */ + offset = p_ctx->priv->io->offset; + + /* Now move to containers, try to find a readers using the file extension to name + mapping first */ + if (fileext && (name = container_for_fileext(fileext)) != NULL && (func = load_reader(&handle, name)) != NULL) + { + status = (*func)(p_ctx); + if(status == VC_CONTAINER_SUCCESS) goto success; + unload_library(handle); + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + } + + /* If there was no suitable mapping, iterate through all readers. */ + for(i = 0; readers[i]; i++) + { + if ((func = load_reader(&handle, readers[i])) != NULL) + { + if(vc_container_io_seek(p_ctx->priv->io, offset) != VC_CONTAINER_SUCCESS) + { + unload_library(handle); + goto error; + } + + status = (*func)(p_ctx); + if(status == VC_CONTAINER_SUCCESS) goto success; + reset_context(p_ctx); + unload_library(handle); + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + } + } + + error: + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + success: + p_ctx->priv->module_handle = handle; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_load_writer(VC_CONTAINER_T *p_ctx, const char *fileext) +{ + const char *name; + void *handle = NULL; + VC_CONTAINER_WRITER_OPEN_FUNC_T func; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FAILED; + unsigned int i; + + vc_container_assert(p_ctx && !p_ctx->priv->module_handle); + + /* Do we have a container mapping for this file extension? */ + if ((name = container_for_fileext(fileext)) != NULL && (func = load_writer(&handle, name)) != NULL) + { + status = (*func)(p_ctx); + if(status == VC_CONTAINER_SUCCESS) goto success; + unload_library(handle); + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + } + + /* If there was no suitable mapping, iterate through all writers. */ + for(i = 0; writers[i]; i++) + { + if ((func = load_writer(&handle, writers[i])) != NULL) + { + status = (*func)(p_ctx); + if(status == VC_CONTAINER_SUCCESS) goto success; + unload_library(handle); + if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error; + } + } + + error: + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + success: + p_ctx->priv->module_handle = handle; + return status; +} + +/*****************************************************************************/ +void vc_container_unload(VC_CONTAINER_T *p_ctx) +{ + if (p_ctx->priv->module_handle) + { + unload_library(p_ctx->priv->module_handle); + p_ctx->priv->module_handle = NULL; + } +} + +/****************************************************************************** +Local Functions +******************************************************************************/ +static void reset_context(VC_CONTAINER_T *p_ctx) +{ + vc_container_assert(p_ctx); + + p_ctx->capabilities = 0; + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + p_ctx->drm = NULL; + p_ctx->priv->module = NULL; + p_ctx->priv->pf_close = NULL; + p_ctx->priv->pf_read = NULL; + p_ctx->priv->pf_write = NULL; + p_ctx->priv->pf_seek = NULL; + p_ctx->priv->pf_control = NULL; + p_ctx->priv->tmp_io = NULL; +} + +/*****************************************************************************/ +static VC_CONTAINER_READER_OPEN_FUNC_T load_reader(void **handle, const char *name) +{ + return load_library(handle, name, NULL, 1); +} + +/*****************************************************************************/ +static VC_CONTAINER_READER_OPEN_FUNC_T load_writer(void **handle, const char *name) +{ + return load_library(handle, name, NULL, 0); +} + +/*****************************************************************************/ +static VC_CONTAINER_READER_OPEN_FUNC_T load_metadata_reader(void **handle, const char *name) +{ + #define DL_PREFIX_METADATA "metadata_" + return load_library(handle, name, DL_PREFIX_METADATA, 1); +} + +#if !defined(ENABLE_CONTAINERS_STANDALONE) + +/*****************************************************************************/ +static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read) +{ + #define DL_PREFIX_RD "reader_" + #define DL_PREFIX_WR "writer_" + const char *entrypt_read = {"reader_open"}; + const char *entrypt_write = {"writer_open"}; + char *dl_name, *entrypt_name; + void *dl_handle; + VC_CONTAINER_READER_OPEN_FUNC_T func = NULL; + unsigned dl_size, ep_size, name_len = strlen(name) + (ext ? strlen(ext) : 0); + + vc_container_assert(read == 0 || read == 1); + + dl_size = strlen(DL_PATH_PREFIX) + MAX(strlen(DL_PREFIX_RD), strlen(DL_PREFIX_WR)) + name_len + strlen(DL_SUFFIX) + 1; + if ((dl_name = malloc(dl_size)) == NULL) + return NULL; + + ep_size = name_len + 1 + MAX(strlen(entrypt_read), strlen(entrypt_write)) + 1; + if ((entrypt_name = malloc(ep_size)) == NULL) + { + free(dl_name); + return NULL; + } + + snprintf(dl_name, dl_size, "%s%s%s%s%s", DL_PATH_PREFIX, read ? DL_PREFIX_RD : DL_PREFIX_WR, ext ? ext : "", name, DL_SUFFIX); + snprintf(entrypt_name, ep_size, "%s_%s%s", name, ext ? ext : "", read ? entrypt_read : entrypt_write); + + if ( (dl_handle = vcos_dlopen(dl_name, VCOS_DL_NOW)) != NULL ) + { + /* Try generic entrypoint name before the mangled, full name */ + func = (VC_CONTAINER_READER_OPEN_FUNC_T)vcos_dlsym(dl_handle, read ? entrypt_read : entrypt_write); +#if !defined(__VIDEOCORE__) /* The following would be pointless on MW/VideoCore */ + if (!func) func = (VC_CONTAINER_READER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name); +#endif + /* Only return handle if symbol found */ + if (func) + *handle = dl_handle; + else + vcos_dlclose(dl_handle); + } + + free(entrypt_name); + free(dl_name); + return func; +} + +/*****************************************************************************/ +static void unload_library(void *handle) +{ + vcos_dlclose(handle); +} + +#else /* !defined(ENABLE_CONTAINERS_STANDALONE) */ + +/*****************************************************************************/ +static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read) +{ + int i; + VC_CONTAINER_PARAM_UNUSED(handle); + VC_CONTAINER_PARAM_UNUSED(ext); + + if (read) + { + for (i = 0; reader_entry_points[i].name; i++) + if (!strcasecmp(reader_entry_points[i].name, name)) + return reader_entry_points[i].func; + + for (i = 0; metadata_reader_entry_points[i].name; i++) + if (!strcasecmp(metadata_reader_entry_points[i].name, name)) + return metadata_reader_entry_points[i].func; + } + else + { + for (i = 0; writer_entry_points[i].name; i++) + if (!strcasecmp(writer_entry_points[i].name, name)) + return writer_entry_points[i].func; + } + + return NULL; +} + +/*****************************************************************************/ +static void unload_library(void *handle) +{ + (void)handle; +} + +#endif /* !defined(ENABLE_CONTAINERS_STANDALONE) */ + +/*****************************************************************************/ +static const char* container_for_fileext(const char *fileext) +{ + int i; + + for( i = 0; fileext && extension_container_mapping[i].extension; i++ ) + { + if (!strcasecmp( fileext, extension_container_mapping[i].extension )) + return extension_container_mapping[i].container; + } + + return fileext; +} diff --git a/containers/core/containers_loader.h b/containers/core/containers_loader.h new file mode 100755 index 0000000..946eaf6 --- /dev/null +++ b/containers/core/containers_loader.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_CONTAINERS_LOADER_H +#define VC_CONTAINERS_LOADER_H + +/** Find and attempt to load & open reader, 'fileext' is a hint that can be used + to speed up loading. */ +VC_CONTAINER_STATUS_T vc_container_load_reader(VC_CONTAINER_T *p_ctx, const char *fileext); + +/** Find and attempt to load & open writer, 'fileext' is a hint used to help in + selecting the appropriate container format. */ +VC_CONTAINER_STATUS_T vc_container_load_writer(VC_CONTAINER_T *p_ctx, const char *fileext); + +void vc_container_unload(VC_CONTAINER_T *p_ctx); + +#endif /* VC_CONTAINERS_LOADER_H */ diff --git a/containers/core/containers_logging.c b/containers/core/containers_logging.c new file mode 100755 index 0000000..5a739a3 --- /dev/null +++ b/containers/core/containers_logging.c @@ -0,0 +1,111 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include "containers/containers.h" +#include "containers/core/containers_private.h" +#include "containers/core/containers_logging.h" + +#ifndef ENABLE_CONTAINERS_STANDALONE +# include "vcos.h" +#endif + +#ifdef __ANDROID__ +#define LOG_TAG "ContainersCore" +#include +#endif + +/* Default verbosity that will be inherited by containers */ +static uint32_t default_verbosity_mask = VC_CONTAINER_LOG_ALL; + +/* By default log everything that's not associated with a container context */ +static uint32_t verbosity_mask = VC_CONTAINER_LOG_ALL; + +void vc_container_log_set_default_verbosity(uint32_t mask) +{ + default_verbosity_mask = mask; +} + +uint32_t vc_container_log_get_default_verbosity(void) +{ + return default_verbosity_mask; +} + +void vc_container_log_set_verbosity(VC_CONTAINER_T *ctx, uint32_t mask) +{ + if(!ctx) verbosity_mask = mask; + else ctx->priv->verbosity = mask; +} + +void vc_container_log(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, ...) +{ + uint32_t verbosity = ctx ? ctx->priv->verbosity : verbosity_mask; + va_list args; + + // Optimise out the call to vc_container_log_vargs etc. when it won't do anything. + if(!(type & verbosity)) return; + + va_start( args, format ); + vc_container_log_vargs(ctx, type, format, args); + va_end( args ); +} + +void vc_container_log_vargs(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, va_list args) +{ + uint32_t verbosity = ctx ? ctx->priv->verbosity : verbosity_mask; + + // If the verbosity is such that the type doesn't need logging quit now. + if(!(type & verbosity)) return; + +#ifdef __ANDROID__ + { + // Default to Android's "verbose" level (doesn't usually come out) + android_LogPriority logLevel = ANDROID_LOG_VERBOSE; + + // Where type suggest a higher level is required update logLevel. + // (Usually type contains only 1 bit as set by the LOG_DEBUG, LOG_ERROR or LOG_INFO macros) + if (type & VC_CONTAINER_LOG_ERROR) + logLevel = ANDROID_LOG_ERROR; + else if (type & VC_CONTAINER_LOG_INFO) + logLevel = ANDROID_LOG_INFO; + else if (type & VC_CONTAINER_LOG_DEBUG) + logLevel = ANDROID_LOG_DEBUG; + + // Actually put the message out. + LOG_PRI_VA(logLevel, LOG_TAG, format, args); + } +#else +#ifndef ENABLE_CONTAINERS_STANDALONE + vcos_vlog(format, args); +#else + vprintf(format, args); printf("\n"); + fflush(0); +#endif +#endif +} diff --git a/containers/core/containers_logging.h b/containers/core/containers_logging.h new file mode 100755 index 0000000..a7c2bd9 --- /dev/null +++ b/containers/core/containers_logging.h @@ -0,0 +1,77 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_LOGGING_H +#define VC_CONTAINERS_LOGGING_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file containers_logging.h + * Logging API used by container readers and writers + */ + +typedef enum { + VC_CONTAINER_LOG_ERROR = 0x01, + VC_CONTAINER_LOG_INFO = 0x02, + VC_CONTAINER_LOG_DEBUG = 0x04, + VC_CONTAINER_LOG_FORMAT = 0x08, + VC_CONTAINER_LOG_ALL = 0xFF +} VC_CONTAINER_LOG_TYPE_T; + +void vc_container_log(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, ...); +void vc_container_log_vargs(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, va_list args); +void vc_container_log_set_verbosity(VC_CONTAINER_T *ctx, uint32_t mask); +void vc_container_log_set_default_verbosity(uint32_t mask); +uint32_t vc_container_log_get_default_verbosity(void); + +#define ENABLE_CONTAINER_LOG_ERROR +#define ENABLE_CONTAINER_LOG_INFO + +#ifdef ENABLE_CONTAINER_LOG_DEBUG +# define LOG_DEBUG(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_DEBUG, __VA_ARGS__) +#else +# define LOG_DEBUG(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx) +#endif + +#ifdef ENABLE_CONTAINER_LOG_ERROR +# define LOG_ERROR(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_ERROR, __VA_ARGS__) +#else +# define LOG_ERROR(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx) +#endif + +#ifdef ENABLE_CONTAINER_LOG_INFO +# define LOG_INFO(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_INFO, __VA_ARGS__) +#else +# define LOG_INFO(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* VC_CONTAINERS_LOGGING_H */ diff --git a/containers/core/containers_private.h b/containers/core/containers_private.h new file mode 100755 index 0000000..8c379c6 --- /dev/null +++ b/containers/core/containers_private.h @@ -0,0 +1,183 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_PRIVATE_H +#define VC_CONTAINERS_PRIVATE_H + +/** \file containers_private.h + * Private interface for container readers and writers + */ + +#include +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_filters.h" +#include "containers/packetizers.h" +#include "containers/core/containers_uri.h" + +#define URI_MAX_LEN 256 + +/** \defgroup VcContainerModuleApi Container Module API + * Private interface for modules implementing container readers and writers */ +/* @{ */ + +/** Track context private to the container reader / writer instance. This private context is used to + * store data which shouldn't be exported by the public API. */ +typedef struct VC_CONTAINER_TRACK_PRIVATE_T +{ + /** Pointer to the private data of the container module in use */ + struct VC_CONTAINER_TRACK_MODULE_T *module; + + /** Pointer to the allocated buffer for the track extradata */ + uint8_t *extradata; + /** Size of the allocated buffer for the track extradata */ + uint32_t extradata_size; + + /** Pointer to the allocated buffer for the track DRM data*/ + uint8_t *drmdata; + /** Size of the allocated buffer for the track DRM data */ + uint32_t drmdata_size; + + /** Packetizer used by this track */ + VC_PACKETIZER_T *packetizer; + +} VC_CONTAINER_TRACK_PRIVATE_T; + +/** Context private to the container reader / writer instance. This private context is used to + * store data which shouldn't be exported by the public API. */ +typedef struct VC_CONTAINER_PRIVATE_T +{ + /** Pointer to the container i/o instance used to read / write to the container */ + struct VC_CONTAINER_IO_T *io; + /** Pointer to the private data of the container module in use */ + struct VC_CONTAINER_MODULE_T *module; + + /** Reads a data packet from a container reader. + * By default, the reader will read whatever packet comes next in the container and update the + * given \ref VC_CONTAINER_PACKET_T structure with this packet's information. + * This behaviour can be changed using the \ref VC_CONTAINER_READ_FLAGS_T.\n + * \ref VC_CONTAINER_READ_FLAG_INFO will instruct the reader to only return information on the + * following packet but not its actual data. The data can be retreived later by issuing another + * read request. + * \ref VC_CONTAINER_READ_FLAG_FORCE_TRACK will force the reader to read the next packet for the + * selected track (as present in the \ref VC_CONTAINER_PACKET_T structure) instead of defaulting + * to reading the packet which comes next in the container. + * \ref VC_CONTAINER_READ_FLAG_SKIP will instruct the reader to skip the next packet. In this case + * it isn't necessary for the caller to pass a pointer to a \ref VC_CONTAINER_PACKET_T structure + * unless the \ref VC_CONTAINER_READ_FLAG_INFO is also given. + * A combination of all these flags can be used. + * + * \param context Pointer to the context of the reader to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * This needs to be partially filled before the call (buffer, buffer_size) + * \param flags Flags controlling the read operation + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_read)( VC_CONTAINER_T *context, + VC_CONTAINER_PACKET_T *packet, VC_CONTAINER_READ_FLAGS_T flags ); + + /** Writes a data packet to a container writer. + * + * \param context Pointer to the context of the writer to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_write)( struct VC_CONTAINER_T *context, + VC_CONTAINER_PACKET_T *packet ); + + /** Seek into a container reader. + * + * \param context Pointer to the context of the reader to use + * \param offset Offset to seek to. Used as an input as well as output value. + * \param mode Seeking mode requested. + * \param flags Flags affecting the seeking operation. + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_seek)( VC_CONTAINER_T *context, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags); + + /** Extensible control function for container readers and writers. + * This function takes a variable number of arguments which will depend on the specific operation. + * + * \param context Pointer to the VC_CONTAINER_T context to use + * \param operation The requested operation + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_control)( VC_CONTAINER_T *context, VC_CONTAINER_CONTROL_T operation, va_list args ); + + /** Closes a container reader / writer module. + * + * \param context Pointer to the context of the instance to close + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_close)( struct VC_CONTAINER_T *context ); + + /** Pointer to container filter instance used for DRM */ + struct VC_CONTAINER_FILTER_T *drm_filter; + + /** Pointer to the container module code and symbols*/ + void *module_handle; + + /** Maximum size of a stream that is being written. + * This is set by the client using the control mechanism */ + int64_t max_size; + + /** Pointer to the temp i/o instance used to write temporary data */ + struct VC_CONTAINER_IO_T *tmp_io; + + /** Current status of the container (only used for writers to prevent trying to write + * more data if one of the writes failed) */ + VC_CONTAINER_STATUS_T status; + + /** Logging verbosity */ + uint32_t verbosity; + + /** Uniform Resource Identifier */ + struct VC_URI_PARTS_T *uri; + + /** Flag specifying whether one of the tracks is being packetized */ + bool packetizing; + + /** Temporary packet structure used to feed data to the packetizer */ + VC_CONTAINER_PACKET_T packetizer_packet; + + /** Temporary buffer used by the packetizer */ + uint8_t *packetizer_buffer; + +} VC_CONTAINER_PRIVATE_T; + +/* Internal functions */ +VC_CONTAINER_TRACK_T *vc_container_allocate_track( VC_CONTAINER_T *context, unsigned int extra_size ); +void vc_container_free_track( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *track ); +VC_CONTAINER_STATUS_T vc_container_track_allocate_extradata( VC_CONTAINER_T *context, + VC_CONTAINER_TRACK_T *p_track, unsigned int extra_size ); +VC_CONTAINER_STATUS_T vc_container_track_allocate_drmdata( VC_CONTAINER_T *context, + VC_CONTAINER_TRACK_T *p_track, unsigned int size ); + +/* @} */ + +#endif /* VC_CONTAINERS_PRIVATE_H */ diff --git a/containers/core/containers_time.h b/containers/core/containers_time.h new file mode 100755 index 0000000..e625c34 --- /dev/null +++ b/containers/core/containers_time.h @@ -0,0 +1,103 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_TIME_H +#define VC_CONTAINERS_TIME_H + +/** \file + * Utility functions to help with timestamping of elementary stream frames + */ + +typedef struct VC_CONTAINER_TIME_T +{ + uint32_t samplerate_num; + uint32_t samplerate_den; + uint32_t time_base; + + uint32_t remainder; + + int64_t time; + +} VC_CONTAINER_TIME_T; + +/*****************************************************************************/ +STATIC_INLINE void vc_container_time_init( VC_CONTAINER_TIME_T *time, uint32_t time_base ) +{ + time->samplerate_num = 0; + time->samplerate_den = 0; + time->remainder = 0; + time->time_base = time_base; + time->time = VC_CONTAINER_TIME_UNKNOWN; +} + +/*****************************************************************************/ +STATIC_INLINE int64_t vc_container_time_get( VC_CONTAINER_TIME_T *time ) +{ + if (time->time == VC_CONTAINER_TIME_UNKNOWN || !time->samplerate_num || !time->samplerate_den) + return VC_CONTAINER_TIME_UNKNOWN; + return time->time + time->remainder * (int64_t)time->time_base * time->samplerate_den / time->samplerate_num; +} + +/*****************************************************************************/ +STATIC_INLINE void vc_container_time_set_samplerate( VC_CONTAINER_TIME_T *time, uint32_t samplerate_num, uint32_t samplerate_den ) +{ + if(time->samplerate_num == samplerate_num && + time->samplerate_den == samplerate_den) + return; + + /* We're changing samplerate, we need to reset our remainder */ + if(time->remainder) + time->time = vc_container_time_get( time ); + time->remainder = 0; + time->samplerate_num = samplerate_num; + time->samplerate_den = samplerate_den; +} + +/*****************************************************************************/ +STATIC_INLINE void vc_container_time_set( VC_CONTAINER_TIME_T *time, int64_t new_time ) +{ + if (new_time == VC_CONTAINER_TIME_UNKNOWN) + return; + time->remainder = 0; + time->time = new_time; +} + +/*****************************************************************************/ +STATIC_INLINE int64_t vc_container_time_add( VC_CONTAINER_TIME_T *time, uint32_t samples ) +{ + uint32_t increment; + + if (time->time == VC_CONTAINER_TIME_UNKNOWN || !time->samplerate_num || !time->samplerate_den) + return VC_CONTAINER_TIME_UNKNOWN; + + samples += time->remainder; + increment = samples * time->samplerate_den / time->samplerate_num; + time->time += increment * time->time_base; + time->remainder = samples - increment * time->samplerate_num / time->samplerate_den; + return vc_container_time_get(time); +} + +#endif /* VC_CONTAINERS_TIME_H */ diff --git a/containers/core/containers_uri.c b/containers/core/containers_uri.c new file mode 100755 index 0000000..b0d3c59 --- /dev/null +++ b/containers/core/containers_uri.c @@ -0,0 +1,1120 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/core/containers_uri.h" + +/*****************************************************************************/ +/* Internal types and definitions */ +/*****************************************************************************/ + +typedef struct VC_URI_QUERY_T +{ + char *name; + char *value; +} VC_URI_QUERY_T; + +struct VC_URI_PARTS_T +{ + char *scheme; /**< Unescaped scheme */ + char *userinfo; /**< Unescaped userinfo */ + char *host; /**< Unescaped host name/IP address */ + char *port; /**< Unescaped port */ + char *path; /**< Unescaped path */ + char *path_extension; /**< Unescaped path extension */ + char *fragment; /**< Unescaped fragment */ + VC_URI_QUERY_T *queries; /**< Array of queries */ + uint32_t num_queries; /**< Number of queries in array */ +}; + +typedef const uint32_t *RESERVED_CHARS_TABLE_T; + +/** Reserved character table for scheme component + * Controls, space, !"#$%&'()*,/:;<=>?@[\]^`{|} and 0x7F and above reserved. */ +static uint32_t scheme_reserved_chars[8] = { + 0xFFFFFFFF, 0xFC0097FF, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for userinfo component + * Controls, space, "#%/<>?@[\]^`{|} and 0x7F and above reserved. */ +static uint32_t userinfo_reserved_chars[8] = { + 0xFFFFFFFF, 0xD000802D, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for host component + * Controls, space, "#%/<>?@\^`{|} and 0x7F and above reserved. */ +static uint32_t host_reserved_chars[8] = { + 0xFFFFFFFF, 0xD000802D, 0x50000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for port component + * Controls, space, !"#$%&'()*+,/:;<=>?@[\]^`{|} and 0x7F and above reserved. */ +static uint32_t port_reserved_chars[8] = { + 0xFFFFFFFF, 0xFC009FFF, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for path component + * Controls, space, "#%<>?[\]^`{|} and 0x7F and above reserved. */ +static uint32_t path_reserved_chars[8] = { + 0xFFFFFFFF, 0xD000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for query component + * Controls, space, "#%<>[\]^`{|} and 0x7F and above reserved. */ +static uint32_t query_reserved_chars[8] = { + 0xFFFFFFFF, 0x5000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +/** Reserved character table for fragment component + * Controls, space, "#%<>[\]^`{|} and 0x7F and above reserved. */ +static uint32_t fragment_reserved_chars[8] = { + 0xFFFFFFFF, 0x5000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +#define URI_RESERVED(C, TABLE) (!!((TABLE)[(unsigned char)(C) >> 5] & (1 << ((C) & 0x1F)))) + +#define SCHEME_DELIMITERS ":/?#" +#define NETWORK_DELIMITERS "@/?#" +#define HOST_PORT_DELIMITERS "/?#" +#define PATH_DELIMITERS "?#" +#define QUERY_DELIMITERS "#" + +/*****************************************************************************/ +/* Internal functions */ +/*****************************************************************************/ + +static char to_hex(int v) +{ + if (v > 9) + return 'A' + v - 10; + return '0' + v; +} + +/*****************************************************************************/ +static uint32_t from_hex(const char *str, uint32_t str_len) +{ + uint32_t val = 0; + + while (str_len--) + { + char c = *str++; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'F') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + c -= 'a' - 10; + else + c = 0; /* Illegal character (not hex) */ + val = (val << 4) + c; + } + + return val; +} + +/*****************************************************************************/ +static uint32_t escaped_length( const char *str, RESERVED_CHARS_TABLE_T reserved ) +{ + uint32_t ii; + uint32_t esclen = 0; + char c; + + for (ii = strlen(str); ii > 0; ii--) + { + c = *str++; + if (URI_RESERVED(c, reserved)) + { + /* Reserved character needs escaping as %xx */ + esclen += 3; + } else { + esclen++; + } + } + + return esclen; +} + +/*****************************************************************************/ +static uint32_t escape_string( const char *str, char *escaped, + RESERVED_CHARS_TABLE_T reserved ) +{ + uint32_t ii; + uint32_t esclen = 0; + + if (!str) + return 0; + + for (ii = strlen(str); ii > 0; ii--) + { + char c = *str++; + + if (URI_RESERVED(c, reserved)) + { + escaped[esclen++] = '%'; + escaped[esclen++] = to_hex((c >> 4) & 0xF); + escaped[esclen++] = to_hex(c & 0xF); + } else { + escaped[esclen++] = c; + } + } + + return esclen; +} + +/*****************************************************************************/ +static uint32_t unescaped_length( const char *str, uint32_t str_len ) +{ + uint32_t ii; + uint32_t unesclen = 0; + + for (ii = 0; ii < str_len; ii++) + { + if (*str++ == '%' && (ii + 2) < str_len) + { + str += 2; /* Should be two hex values next */ + ii += 2; + } + unesclen++; + } + + return unesclen; +} + +/*****************************************************************************/ +static void unescape_string( const char *str, uint32_t str_len, char *unescaped ) +{ + uint32_t ii; + + for (ii = 0; ii < str_len; ii++) + { + char c = *str++; + + if (c == '%' && (ii + 2) < str_len ) + { + c = (char)(from_hex(str, 2) & 0xFF); + str += 2; + ii += 2; + } + *unescaped++ = c; + } + + *unescaped = '\0'; +} + +/*****************************************************************************/ +static char *create_unescaped_string( const char *escstr, uint32_t esclen ) +{ + char *unescstr; + + unescstr = (char *)malloc(unescaped_length(escstr, esclen) + 1); /* Allow for NUL */ + if (unescstr) + unescape_string(escstr, esclen, unescstr); + + return unescstr; +} + +/*****************************************************************************/ +static bool duplicate_string( const char *src, char **p_dst ) +{ + if (*p_dst) + free(*p_dst); + + if (src) + { + size_t str_size = strlen(src) + 1; + + *p_dst = (char *)malloc(str_size); + if (!*p_dst) + return false; + + memcpy(*p_dst, src, str_size); + } else + *p_dst = NULL; + + return true; +} + +/*****************************************************************************/ +static void release_string( char **str ) +{ + if (*str) + { + free(*str); + *str = NULL; + } +} + +/*****************************************************************************/ +static void to_lower_string( char *str ) +{ + char c; + + while ((c = *str) != '\0') + { + if (c >= 'A' && c <= 'Z') + *str = c - 'A' + 'a'; + str++; + } +} + +/*****************************************************************************/ +static const char *vc_uri_find_delimiter(const char *str, const char *delimiters) +{ + const char *ptr = str; + char c; + + while ((c = *ptr) != 0) + { + if (strchr(delimiters, c) != 0) + break; + ptr++; + } + + return ptr; +} + +/*****************************************************************************/ +static void vc_uri_set_path_extension(VC_URI_PARTS_T *p_uri) +{ + char *end; + + if (!p_uri) + return; + + p_uri->path_extension = NULL; + + if (!p_uri->path) + return; + + /* Look for the magic dot */ + for (end = p_uri->path + strlen(p_uri->path); *end != '.'; end--) + if (end == p_uri->path || *end == '/' || *end == '\\') + return; + + p_uri->path_extension = end + 1; +} + +/*****************************************************************************/ +static bool parse_authority( VC_URI_PARTS_T *p_uri, const char *str, + uint32_t str_len, const char *userinfo_end ) +{ + const char *marker = userinfo_end; + const char *str_end = str + str_len; + char c; + + if (marker) + { + p_uri->userinfo = create_unescaped_string(str, marker - str); + if (!p_uri->userinfo) + return false; + str = marker + 1; /* Past '@' character */ + } + + if (*str == '[') /* IPvFuture / IPv6 address */ + { + /* Find end of address marker */ + for (marker = str; marker < str_end; marker++) + { + c = *marker; + if (c == ']') + break; + } + + if (marker < str_end) + marker++; /* Found marker, move to next character */ + } else { + /* Find port value marker*/ + for (marker = str; marker < str_end; marker++) + { + c = *marker; + if (c == ':') + break; + } + } + + /* Always store the host, even if empty, to trigger the "://" form of URI */ + p_uri->host = create_unescaped_string(str, marker - str); + if (!p_uri->host) + return false; + to_lower_string(p_uri->host); /* Host names are case-insensitive */ + + if (*marker == ':') + { + str = marker + 1; + p_uri->port = create_unescaped_string(str, str_end - str); + if (!p_uri->port) + return false; + } + + return true; +} + +/*****************************************************************************/ +static bool store_query( VC_URI_PARTS_T *p_uri, const char *name_start, + const char *equals_ptr, const char *query_end) +{ + uint32_t name_len, value_len; + + if (equals_ptr) + { + name_len = equals_ptr - name_start; + value_len = query_end - equals_ptr - 1; /* Don't include '=' itself */ + } else { + name_len = query_end - name_start; + value_len = 0; + } + + /* Only store something if there is a name */ + if (name_len) + { + char *name, *value = NULL; + VC_URI_QUERY_T *p_query; + + if (equals_ptr) + { + value = create_unescaped_string(equals_ptr + 1, value_len); + if (!value) + return false; + equals_ptr = query_end; + } + + name = create_unescaped_string(name_start, name_len); + if (!name) + { + if (value) + free(value); + return false; + } + + /* Store query data in URI structure */ + p_query = &p_uri->queries[ p_uri->num_queries++ ]; + p_query->name = name; + p_query->value = value; + } + + return true; +} + +/*****************************************************************************/ +static bool parse_query( VC_URI_PARTS_T *p_uri, const char *str, uint32_t str_len ) +{ + uint32_t ii; + uint32_t query_count; + VC_URI_QUERY_T *queries; + const char *name_start = str; + const char *equals_ptr = NULL; + char c; + + if (!str_len) + return true; + + /* Scan for the number of query items, so array can be allocated the right size */ + query_count = 1; /* At least */ + for (ii = 0; ii < str_len; ii++) + { + c = str[ii]; + + if (c == '&' || c ==';') + query_count++; + } + + queries = (VC_URI_QUERY_T *)malloc(query_count * sizeof(VC_URI_QUERY_T)); + if (!queries) + return false; + + p_uri->queries = queries; + + /* Go back and parse the string for each query item and store in array */ + for (ii = 0; ii < str_len; ii++) + { + c = *str; + + /* Take first '=' as break between name and value */ + if (c == '=' && !equals_ptr) + equals_ptr = str; + + /* If at the end of the name or name/value pair */ + if (c == '&' || c ==';') + { + if (!store_query(p_uri, name_start, equals_ptr, str)) + return false; + + equals_ptr = NULL; + name_start = str + 1; + } + + str++; + } + + return store_query(p_uri, name_start, equals_ptr, str); +} + +/*****************************************************************************/ +static uint32_t calculate_uri_length(const VC_URI_PARTS_T *p_uri) +{ + uint32_t length = 0; + uint32_t count; + + /* With no scheme, assume this is a plain path (without escaping) */ + if (!p_uri->scheme) + return p_uri->path ? strlen(p_uri->path) : 0; + + length += escaped_length(p_uri->scheme, scheme_reserved_chars); + length++; /* for the colon */ + + if (p_uri->host) + { + length += escaped_length(p_uri->host, host_reserved_chars) + 2; /* for the double slash */ + if (p_uri->userinfo) + length += escaped_length(p_uri->userinfo, userinfo_reserved_chars) + 1; /* for the '@' */ + if (p_uri->port) + length += escaped_length(p_uri->port, port_reserved_chars) + 1; /* for the ':' */ + } + + if (p_uri->path) + length += escaped_length(p_uri->path, path_reserved_chars); + + count = p_uri->num_queries; + if (count) + { + VC_URI_QUERY_T * queries = p_uri->queries; + + while (count--) + { + /* The name is preceded by either the '?' or the '&' */ + length += escaped_length(queries->name, query_reserved_chars) + 1; + + /* The value is optional, but if present will require an '=' */ + if (queries->value) + length += escaped_length(queries->value, query_reserved_chars) + 1; + queries++; + } + } + + if (p_uri->fragment) + length += escaped_length(p_uri->fragment, fragment_reserved_chars) + 1; /* for the '#' */ + + return length; +} + +/*****************************************************************************/ +static void build_uri(const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size) +{ + uint32_t count; + + /* With no scheme, assume this is a plain path (without escaping) */ + if (!p_uri->scheme) + { + if (p_uri->path) + strncpy(buffer, p_uri->path, buffer_size); + else + buffer[0] = '\0'; + return; + } + + buffer += escape_string(p_uri->scheme, buffer, scheme_reserved_chars); + *buffer++ = ':'; + + if (p_uri->host) + { + *buffer++ = '/'; + *buffer++ = '/'; + if (p_uri->userinfo) + { + buffer += escape_string(p_uri->userinfo, buffer, userinfo_reserved_chars); + *buffer++ = '@'; + } + buffer += escape_string(p_uri->host, buffer, host_reserved_chars); + if (p_uri->port) + { + *buffer++ = ':'; + buffer += escape_string(p_uri->port, buffer, port_reserved_chars); + } + } + + if (p_uri->path) + buffer += escape_string(p_uri->path, buffer, path_reserved_chars); + + count = p_uri->num_queries; + if (count) + { + VC_URI_QUERY_T * queries = p_uri->queries; + + *buffer++ = '?'; + while (count--) + { + buffer += escape_string(queries->name, buffer, query_reserved_chars); + + if (queries->value) + { + *buffer++ = '='; + buffer += escape_string(queries->value, buffer, query_reserved_chars); + } + + /* Add separator if there is another item to add */ + if (count) + *buffer++ = '&'; + + queries++; + } + } + + if (p_uri->fragment) + { + *buffer++ = '#'; + buffer += escape_string(p_uri->fragment, buffer, fragment_reserved_chars); + } + + *buffer = '\0'; +} + +/*****************************************************************************/ +static bool vc_uri_copy_base_path( const VC_URI_PARTS_T *base_uri, + VC_URI_PARTS_T *relative_uri ) +{ + const char *base_path = vc_uri_path(base_uri); + + /* No path set (or empty), copy from base */ + if (!vc_uri_set_path(relative_uri, base_path)) + return false; + + /* If relative path has no queries, copy base queries across */ + if (!vc_uri_num_queries(relative_uri)) + { + uint32_t base_queries = vc_uri_num_queries(base_uri); + const char *name, *value; + uint32_t ii; + + for (ii = 0; ii < base_queries; ii++) + { + vc_uri_query(base_uri, ii, &name, &value); + if (!vc_uri_add_query(relative_uri, name, value)) + return false; + } + } + + return true; +} + +/*****************************************************************************/ +static void vc_uri_remove_single_dot_segments( char *path_str ) +{ + char *slash = path_str - 1; + + while (slash++) + { + if (*slash == '.') + { + switch (slash[1]) + { + case '/': /* Single dot segment, remove it */ + memmove(slash, slash + 2, strlen(slash + 2) + 1); + break; + case '\0': /* Trailing single dot, remove it */ + *slash = '\0'; + break; + default: /* Something else (e.g. ".." or ".foo") */ + ; /* Do nothing */ + } + } + slash = strchr(slash, '/'); + } +} + +/*****************************************************************************/ +static void vc_uri_remove_double_dot_segments( char *path_str ) +{ + char *previous_segment = path_str; + char *slash; + + if (previous_segment[0] == '/') + previous_segment++; + + /* Remove strings of the form "/../" (or "/.." at the end of the path) + * as long as is not itself ".." */ + slash = strchr(previous_segment, '/'); + while (slash) + { + if (previous_segment[0] != '.' || previous_segment[1] != '.' || previous_segment[2] != '/') + { + if (slash[1] == '.' && slash[2] == '.') + { + bool previous_segment_removed = true; + + switch (slash[3]) + { + case '/': /* "/../" inside path, snip it and last segment out */ + memmove(previous_segment, slash + 4, strlen(slash + 4) + 1); + break; + case '\0': /* Trailing "/.." on path, just terminate path at last segment */ + *previous_segment = '\0'; + break; + default: /* Not a simple ".." segment, so skip over it */ + previous_segment_removed = false; + } + + if (previous_segment_removed) + { + /* The segment just removed was the first one in the path (optionally + * prefixed by a slash), so no more can be removed: stop. */ + if (previous_segment < path_str + 2) + break; + + /* Move back to slash before previous segment, or the start of the path */ + slash = previous_segment - 1; + while (--slash >= path_str && *slash != '/') + ; /* Everything done in the while */ + } + } + } + previous_segment = slash + 1; + slash = strchr(previous_segment, '/'); + } +} + +/*****************************************************************************/ +/* API functions */ +/*****************************************************************************/ + +VC_URI_PARTS_T *vc_uri_create( void ) +{ + VC_URI_PARTS_T *p_uri; + + p_uri = (VC_URI_PARTS_T *)malloc(sizeof(VC_URI_PARTS_T)); + if (p_uri) + { + memset(p_uri, 0, sizeof(VC_URI_PARTS_T)); + } + + return p_uri; +} + +/*****************************************************************************/ +void vc_uri_clear( VC_URI_PARTS_T *p_uri ) +{ + if (!p_uri) + return; + + release_string(&p_uri->scheme); + release_string(&p_uri->userinfo); + release_string(&p_uri->host); + release_string(&p_uri->port); + release_string(&p_uri->path); + release_string(&p_uri->fragment); + + if (p_uri->queries) + { + VC_URI_QUERY_T *queries = p_uri->queries; + uint32_t count = p_uri->num_queries; + + while (count--) + { + release_string(&queries[count].name); + release_string(&queries[count].value); + } + + free(queries); + p_uri->queries = NULL; + p_uri->num_queries = 0; + } +} + +/*****************************************************************************/ +void vc_uri_release( VC_URI_PARTS_T *p_uri ) +{ + if (!p_uri) + return; + + vc_uri_clear(p_uri); + + free(p_uri); +} + +/*****************************************************************************/ +bool vc_uri_parse( VC_URI_PARTS_T *p_uri, const char *uri ) +{ + const char *marker; + uint32_t len; + + if (!p_uri || !uri) + return false; + + vc_uri_clear(p_uri); + + /* URI = scheme ":" hier_part [ "?" query ] [ "#" fragment ] */ + + /* Find end of scheme, or another separator */ + marker = vc_uri_find_delimiter(uri, SCHEME_DELIMITERS); + + if (*marker == ':') + { + len = (marker - uri); + if (isalpha((int)*uri) && len == 1 && marker[1] == '\\') + { + /* Looks like a bare, absolute DOS/Windows filename with a drive letter */ + /* coverity[double_free] Pointer freed and set to NULL */ + bool ret = duplicate_string(uri, &p_uri->path); + vc_uri_set_path_extension(p_uri); + return ret; + } + + p_uri->scheme = create_unescaped_string(uri, len); + if (!p_uri->scheme) + goto error; + + to_lower_string(p_uri->scheme); /* Schemes should be handled case-insensitively */ + uri = marker + 1; + } + + if (uri[0] == '/' && uri[1] == '/') /* hier-part includes authority */ + { + const char *userinfo_end = NULL; + + /* authority = [ userinfo "@" ] host [ ":" port ] */ + uri += 2; + + marker = vc_uri_find_delimiter(uri, NETWORK_DELIMITERS); + if (*marker == '@') + { + userinfo_end = marker; + marker = vc_uri_find_delimiter(marker + 1, HOST_PORT_DELIMITERS); + } + + if (!parse_authority(p_uri, uri, marker - uri, userinfo_end)) + goto error; + uri = marker; + } + + /* path */ + marker = vc_uri_find_delimiter(uri, PATH_DELIMITERS); + len = marker - uri; + if (len) + { + p_uri->path = create_unescaped_string(uri, len); + vc_uri_set_path_extension(p_uri); + if (!p_uri->path) + goto error; + } + + /* query */ + if (*marker == '?') + { + uri = marker + 1; + marker = vc_uri_find_delimiter(uri, QUERY_DELIMITERS); + if (!parse_query(p_uri, uri, marker - uri)) + goto error; + } + + /* fragment */ + if (*marker == '#') + { + uri = marker + 1; + p_uri->fragment = create_unescaped_string(uri, strlen(uri)); + if (!p_uri->fragment) + goto error; + } + + return true; + +error: + vc_uri_clear(p_uri); + return false; +} + +/*****************************************************************************/ +uint32_t vc_uri_build( const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size ) +{ + uint32_t required_length; + + if (!p_uri) + return 0; + + required_length = calculate_uri_length(p_uri); + if (buffer && required_length < buffer_size) /* Allow for NUL */ + build_uri(p_uri, buffer, buffer_size); + + return required_length; +} + +/*****************************************************************************/ +const char *vc_uri_scheme( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->scheme : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_userinfo( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->userinfo : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_host( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->host : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_port( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->port : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_path( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->path : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_path_extension( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->path_extension : NULL; +} + +/*****************************************************************************/ +const char *vc_uri_fragment( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->fragment : NULL; +} + +/*****************************************************************************/ +uint32_t vc_uri_num_queries( const VC_URI_PARTS_T *p_uri ) +{ + return p_uri ? p_uri->num_queries : 0; +} + +/*****************************************************************************/ +void vc_uri_query( const VC_URI_PARTS_T *p_uri, uint32_t index, const char **p_name, const char **p_value ) +{ + const char *name = NULL; + const char *value = NULL; + + if (p_uri) + { + if (index < p_uri->num_queries) + { + name = p_uri->queries[index].name; + value = p_uri->queries[index].value; + } + } + + if (p_name) + *p_name = name; + if (p_value) + *p_value = value; +} + +/*****************************************************************************/ +bool vc_uri_find_query( VC_URI_PARTS_T *p_uri, uint32_t *p_index, const char *name, const char **p_value ) +{ + unsigned int i = p_index ? *p_index : 0; + + if (!p_uri) + return false; + + for (; name && i < p_uri->num_queries; i++) + { + if (!strcmp(name, p_uri->queries[i].name)) + { + if (p_value) + *p_value = p_uri->queries[i].value; + if (p_index) + *p_index = i; + return true; + } + } + + return false; +} + +/*****************************************************************************/ +bool vc_uri_set_scheme( VC_URI_PARTS_T *p_uri, const char *scheme ) +{ + return p_uri ? duplicate_string(scheme, &p_uri->scheme) : false; +} + +/*****************************************************************************/ +bool vc_uri_set_userinfo( VC_URI_PARTS_T *p_uri, const char *userinfo ) +{ + return p_uri ? duplicate_string(userinfo, &p_uri->userinfo) : false; +} + +/*****************************************************************************/ +bool vc_uri_set_host( VC_URI_PARTS_T *p_uri, const char *host ) +{ + return p_uri ? duplicate_string(host, &p_uri->host) : false; +} + +/*****************************************************************************/ +bool vc_uri_set_port( VC_URI_PARTS_T *p_uri, const char *port ) +{ + return p_uri ? duplicate_string(port, &p_uri->port) : false; +} + +/*****************************************************************************/ +bool vc_uri_set_path( VC_URI_PARTS_T *p_uri, const char *path ) +{ + bool ret = p_uri ? duplicate_string(path, &p_uri->path) : false; + vc_uri_set_path_extension(p_uri); + return ret; +} + +/*****************************************************************************/ +bool vc_uri_set_fragment( VC_URI_PARTS_T *p_uri, const char *fragment ) +{ + return p_uri ? duplicate_string(fragment, &p_uri->fragment) : false; +} + +/*****************************************************************************/ +bool vc_uri_add_query( VC_URI_PARTS_T *p_uri, const char *name, const char *value ) +{ + VC_URI_QUERY_T *queries; + uint32_t count; + + if (!p_uri || !name) + return false; + + count = p_uri->num_queries; + if (p_uri->queries) + queries = (VC_URI_QUERY_T *)realloc(p_uri->queries, (count + 1) * sizeof(VC_URI_QUERY_T)); + else + queries = (VC_URI_QUERY_T *)malloc(sizeof(VC_URI_QUERY_T)); + + if (!queries) + return false; + + /* Always store the pointer, in case it has changed, and even if we fail to copy name/value */ + p_uri->queries = queries; + queries[count].name = NULL; + queries[count].value = NULL; + + if (duplicate_string(name, &queries[count].name)) + { + if (duplicate_string(value, &queries[count].value)) + { + /* Successful exit path */ + p_uri->num_queries++; + return true; + } + + release_string(&queries[count].name); + } + + return false; +} + +/*****************************************************************************/ +bool vc_uri_merge( const VC_URI_PARTS_T *base_uri, VC_URI_PARTS_T *relative_uri ) +{ + bool success = true; + const char *relative_path; + + /* If scheme is already set, the URI is already absolute */ + if (relative_uri->scheme) + return true; + + /* Otherwise, copy the base scheme */ + if (!duplicate_string(base_uri->scheme, &relative_uri->scheme)) + return false; + + /* If any of the network info is set, use the rest of the relative URI as-is */ + if (relative_uri->host || relative_uri->port || relative_uri->userinfo) + return true; + + /* Otherwise, copy the base network info */ + if (!duplicate_string(base_uri->host, &relative_uri->host) || + !duplicate_string(base_uri->port, &relative_uri->port) || + !duplicate_string(base_uri->userinfo, &relative_uri->userinfo)) + return false; + + relative_path = relative_uri->path; + + if (!relative_path || !*relative_path) + { + /* No relative path (could be queries and/or fragment), so take base path */ + success = vc_uri_copy_base_path(base_uri, relative_uri); + } + else if (*relative_path != '/') + { + const char *base_path = base_uri->path; + char *merged_path; + char *slash; + size_t len; + + /* Path is relative, merge in with base path */ + if (!base_path || !*base_path) + { + if (relative_uri->host || relative_uri->port || relative_uri->userinfo) + base_path = "/"; /* Need a separator to split network info from path */ + else + base_path = ""; + } + + len = strlen(base_path) + strlen(relative_path) + 1; + + /* Allocate space for largest possible combined path */ + merged_path = (char *)malloc(len); + if (!merged_path) + return false; + + strncpy(merged_path, base_path, len); + + slash = strrchr(merged_path, '/'); /* Note: reverse search */ + if (*relative_path == ';') + { + char *semi; + + /* Relative path is just parameters, so remove any base parameters in final segment */ + if (!slash) + slash = merged_path; + semi = strchr(slash, ';'); + if (semi) + semi[0] = '\0'; + } else { + /* Remove final segment */ + if (slash) + slash[1] = '\0'; + else + merged_path[0] = '\0'; + } + strncat(merged_path, relative_path, len - strlen(merged_path) - 1); + + vc_uri_remove_single_dot_segments(merged_path); + vc_uri_remove_double_dot_segments(merged_path); + + success = duplicate_string(merged_path, &relative_uri->path); + + free(merged_path); + } + /* Otherwise path is absolute, which can be left as-is */ + + return success; +} diff --git a/containers/core/containers_uri.h b/containers/core/containers_uri.h new file mode 100755 index 0000000..77a06e7 --- /dev/null +++ b/containers/core/containers_uri.h @@ -0,0 +1,241 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_CONTAINERS_URI_H +#define VC_CONTAINERS_URI_H + +/** \file containers_uri.h + * API for parsing and building URI strings as described in RFC3986. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "containers/containers.h" + +typedef struct VC_URI_PARTS_T VC_URI_PARTS_T; + +/** Create an empty URI structure. + * + * \return The new URI structure. */ +VC_URI_PARTS_T *vc_uri_create( void ); + +/** Destroy a URI structure. + * + * \param p_uri Pointer to a URI parts structure. */ +void vc_uri_release( VC_URI_PARTS_T *p_uri ); + +/** Clear a URI structure. + * Any URI component strings held are released, but the structure itself is not. + * + * \param p_uri Pointer to a URI parts structure. */ +void vc_uri_clear( VC_URI_PARTS_T *p_uri ); + +/** Parses and unescapes a URI into the component parts. + * + * \param p_uri Pointer to a URI parts structure. + * \param uri Pointer to a URI string to be parsed. + * \return True if successful, false if not. */ +bool vc_uri_parse( VC_URI_PARTS_T *p_uri, const char *uri ); + +/** Builds the URI component parts into a URI string. + * If buffer is NULL, or buffer_size is too small, nothing is written to the + * buffer but the required string length is still returned. buffer_size must be + * at least one more than the value returned. + * + * \param p_uri Pointer to a URI parts structure. + * \param buffer Pointer to where the URI string is to be built, or NULL. + * \param buffer_size Number of bytes available in the buffer. + * \return The length of the URI string. */ +uint32_t vc_uri_build( const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size ); + +/** Retrieves the scheme of the URI. + * The string is valid until either the scheme is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the scheme string. */ +const char *vc_uri_scheme( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the userinfo of the URI. + * The string is valid until either the userinfo is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the userinfo string. */ +const char *vc_uri_userinfo( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the host of the URI. + * The string is valid until either the host is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the host string. */ +const char *vc_uri_host( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the port of the URI. + * The string is valid until either the port is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the port string. */ +const char *vc_uri_port( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the path of the URI. + * The string is valid until either the path is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the path string. */ +const char *vc_uri_path( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the extension part of the path of the URI. + * The string is valid until either the path is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the extension string. */ +const char *vc_uri_path_extension( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves the fragment of the URI. + * The string is valid until either the fragment is changed or the URI is released. + * + * \param p_uri The parsed URI. + * \return Pointer to the fragment string. */ +const char *vc_uri_fragment( const VC_URI_PARTS_T *p_uri ); + +/** Returns the number of query name/value pairs stored. + * + * \param p_uri The parsed URI. + * \return Number of queries stored. */ +uint32_t vc_uri_num_queries( const VC_URI_PARTS_T *p_uri ); + +/** Retrieves a given query's name and value + * If either p_name or p_value are NULL, that part of the query is not returned, + * otherwise it is set to the address of the string (which may itself be NULL). + * + * \param p_uri The parsed URI. + * \param index Selects the query to get. + * \param p_name Address of a string pointer to receive query name, or NULL. + * \param p_value Address of a string pointer to receive query value, or NULL. */ +void vc_uri_query( const VC_URI_PARTS_T *p_uri, uint32_t index, const char **p_name, const char **p_value ); + +/** Finds a specific query in the array. + * If p_index is NULL, then it is assumed the search should start at index 0, + * otherwise the search will start at the specified index and the index will + * be updated on return to point to the query which has been found. + * If p_value is NULL, that part of the query is not returned, + * otherwise it is set to the address of the value string (which may itself be NULL). + * + * \param p_uri Pointer to a URI parts structure. + * \param p_index Index from which to start the search. May be NULL. + * \param name Unescaped query name. + * \param value Unescaped query value. May be NULL. + * \return True if successful, false if not. */ +bool vc_uri_find_query( VC_URI_PARTS_T *p_uri, uint32_t *p_index, const char *name, const char **p_value ); + +/** Sets the scheme of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param scheme Pointer to the new scheme string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_scheme( VC_URI_PARTS_T *p_uri, const char *scheme ); + +/** Sets the userinfo of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param userinfo Pointer to the new userinfo string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_userinfo( VC_URI_PARTS_T *p_uri, const char *userinfo ); + +/** Sets the host of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param host Pointer to the new host string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_host( VC_URI_PARTS_T *p_uri, const char *host ); + +/** Sets the port of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param port Pointer to the new port string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_port( VC_URI_PARTS_T *p_uri, const char *port ); + +/** Sets the path of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param path Pointer to the new path string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_path( VC_URI_PARTS_T *p_uri, const char *path ); + +/** Sets the fragment of the URI. + * The string will be copied and stored in the URI, releasing and replacing + * any existing string. If NULL is passed, any existing string shall simply be + * released. + * + * \param p_uri The parsed URI. + * \param fragment Pointer to the new fragment string, or NULL. + * \return True if successful, false on memory allocation failure. */ +bool vc_uri_set_fragment( VC_URI_PARTS_T *p_uri, const char *fragment ); + +/** Adds an query to the array. + * Note that the queries pointer may change after this function is called. + * May fail due to memory allocation failure or invalid parameters. + * + * \param p_uri Pointer to a URI parts structure. + * \param name Unescaped query name. + * \param value Unescaped query value. May be NULL. + * \return True if successful, false if not. */ +bool vc_uri_add_query( VC_URI_PARTS_T *p_uri, const char *name, const char *value ); + +/** Merge a base URI and a relative URI. + * In general, where the relative URI does not have a given element, the + * corresponding element from the base URI is used. See RFC1808. + * The combined URI is left in relative_uri. If the function is unsuccessful, + * the relative_uri may have been partially modified. + * + * \param base_uri Pointer to the base URI parts structure. + * \param relative_uri Pointer to the relative URI parts structure. + * \return True if successful, false if not. */ +bool vc_uri_merge( const VC_URI_PARTS_T *base_uri, VC_URI_PARTS_T *relative_uri ); + +#ifdef __cplusplus +} +#endif + +#endif /* VC_CONTAINERS_URI_H */ diff --git a/containers/core/containers_utils.c b/containers/core/containers_utils.c new file mode 100755 index 0000000..477093f --- /dev/null +++ b/containers/core/containers_utils.c @@ -0,0 +1,366 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_utils.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define BITMAPINFOHEADER_SIZE_MAX 40 +#define MAX_EXTENSION_SIZE 4 + +#define VC_CONTAINER_ES_FORMAT_MAGIC ((uint32_t)VC_FOURCC('m','a','g','f')) +#define EXTRADATA_SIZE_DEFAULT 32 +#define EXTRADATA_SIZE_MAX (10*1024) + +/*****************************************************************************/ +typedef struct VC_CONTAINER_ES_FORMAT_PRIVATE_T +{ + VC_CONTAINER_ES_FORMAT_T format; + VC_CONTAINER_ES_SPECIFIC_FORMAT_T type; + + uint32_t magic; + + unsigned int extradata_size; + uint8_t *extradata; + + uint8_t buffer[EXTRADATA_SIZE_DEFAULT]; + +} VC_CONTAINER_ES_FORMAT_PRIVATE_T; + +/*****************************************************************************/ +VC_CONTAINER_ES_FORMAT_T *vc_container_format_create(unsigned int extradata_size) +{ + VC_CONTAINER_ES_FORMAT_PRIVATE_T *private; + VC_CONTAINER_STATUS_T status; + + private = malloc(sizeof(*private)); + if(!private) return 0; + memset(private, 0, sizeof(*private)); + + private->magic = VC_CONTAINER_ES_FORMAT_MAGIC; + private->format.type = (void *)&private->type; + private->extradata_size = EXTRADATA_SIZE_DEFAULT; + + status = vc_container_format_extradata_alloc(&private->format, extradata_size); + if(status != VC_CONTAINER_SUCCESS) + { + free(private); + return NULL; + } + + return &private->format; +} + +/*****************************************************************************/ +void vc_container_format_delete(VC_CONTAINER_ES_FORMAT_T *format) +{ + VC_CONTAINER_ES_FORMAT_PRIVATE_T *private = (VC_CONTAINER_ES_FORMAT_PRIVATE_T *)format; + vc_container_assert(private->magic == VC_CONTAINER_ES_FORMAT_MAGIC); + if(private->extradata) free(private->extradata); + free(private); +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_format_extradata_alloc( + VC_CONTAINER_ES_FORMAT_T *format, unsigned int size) +{ + VC_CONTAINER_ES_FORMAT_PRIVATE_T *private = (VC_CONTAINER_ES_FORMAT_PRIVATE_T *)format; + vc_container_assert(private->magic == VC_CONTAINER_ES_FORMAT_MAGIC); + + /* Sanity check the size requested */ + if(size > EXTRADATA_SIZE_MAX) + return VC_CONTAINER_ERROR_CORRUPTED; + + /* Allocate memory if needed */ + if(private->extradata_size < size) + { + if(private->extradata) free(private->extradata); + private->extradata = malloc(size); + if(!private->extradata) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + private->extradata_size = size; + } + + /* Set the fields in the actual format structure */ + if(private->extradata) private->format.extradata = private->extradata; + else private->format.extradata = private->buffer; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_format_copy( VC_CONTAINER_ES_FORMAT_T *p_out, + VC_CONTAINER_ES_FORMAT_T *p_in, + unsigned int extra_buffer_size) +{ + void *type = p_out->type; + uint8_t *extradata = p_out->extradata; + + /* Check we have a sufficient buffer to copy the extra data */ + if(p_in->extradata_size > extra_buffer_size || + (p_in->extradata_size && !p_out->extradata)) + return VC_CONTAINER_ERROR_BUFFER_TOO_SMALL; + + *p_out->type = *p_in->type; + *p_out = *p_in; + p_out->type = type; + p_out->extradata = extradata; + if(p_in->extradata_size) + memcpy(p_out->extradata, p_in->extradata, p_in->extradata_size); + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +int utf8_from_charset(const char *charset, char *out, unsigned int out_size, + const void *in, unsigned int in_size) +{ + unsigned int i; + const uint16_t *in16 = (const uint16_t *)in; + const uint8_t *in8 = (const uint8_t *)in; + + if(out_size < 1) return 1; + if(!strcmp(charset, "UTF16-LE")) goto utf16le; + if(!strcmp(charset, "UTF8")) goto utf8; + else return 1; + + utf16le: + for(i = 0; i < in_size / 2 && in16[i] && i < out_size - 1; i++) + { + out[i] = in16[i]; + } + out[i] = 0; + return 0; + + utf8: + for(i = 0; i < in_size && in8[i] && i < out_size - 1; i++) + { + out[i] = in8[i]; + } + out[i] = 0; + return 0; +} + +/*****************************************************************************/ +unsigned int vc_container_es_format_to_waveformatex(VC_CONTAINER_ES_FORMAT_T *format, + uint8_t *buffer, unsigned int buffer_size) +{ + uint16_t waveformat = codec_to_waveformat(format->codec); + + if(format->es_type != VC_CONTAINER_ES_TYPE_AUDIO || + waveformat == WAVE_FORMAT_UNKNOWN) return 0; + + if(!buffer) return format->extradata_size + 18; + + if(buffer_size < format->extradata_size + 18) return 0; + + /* Build a waveformatex header */ + buffer[0] = waveformat; + buffer[1] = waveformat >> 8; + buffer[2] = format->type->audio.channels; + buffer[3] = 0; + buffer[4] = (format->type->audio.sample_rate >> 0) & 0xFF; + buffer[5] = (format->type->audio.sample_rate >> 8) & 0xFF; + buffer[6] = (format->type->audio.sample_rate >> 16) & 0xFF; + buffer[7] = (format->type->audio.sample_rate >> 24) & 0xFF; + buffer[8] = (format->bitrate >> 3) & 0xFF; + buffer[9] = (format->bitrate >> 11) & 0xFF; + buffer[10] = (format->bitrate >> 19) & 0xFF; + buffer[11] = (format->bitrate >> 27) & 0xFF; + buffer[12] = (format->type->audio.block_align >> 0) & 0xFF; + buffer[13] = (format->type->audio.block_align >> 8) & 0xFF; + buffer[14] = (format->type->audio.bits_per_sample >> 0) & 0xFF; + buffer[15] = (format->type->audio.bits_per_sample >> 8) & 0xFF; + buffer[16] = (format->extradata_size >> 0) & 0xFF; + buffer[17] = (format->extradata_size >> 8) & 0xFF; + memcpy(buffer+18, format->extradata, format->extradata_size); + return format->extradata_size + 18; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_waveformatex_to_es_format(uint8_t *p, + unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, + VC_CONTAINER_ES_FORMAT_T *format) +{ + VC_CONTAINER_FOURCC_T fourcc; + uint32_t waveformat_id; + + if(!p || buffer_size < 16) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + waveformat_id = (p[1] << 8) | p[0]; + fourcc = waveformat_to_codec(waveformat_id); + + /* Read the waveformatex header */ + if(extra_offset) *extra_offset = 16; + if(extra_size) *extra_size = 0; + format->type->audio.channels = p[2]; + format->type->audio.sample_rate = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; + format->bitrate = ((p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]) * 8; + format->type->audio.block_align = (p[13] << 8) | p[12]; + format->type->audio.bits_per_sample = (p[15] << 8) | p[14]; + + if(waveformat_id == WAVE_FORMAT_PCM && format->type->audio.bits_per_sample == 8) + fourcc = VC_CONTAINER_CODEC_PCM_UNSIGNED_LE; + + if(buffer_size >= 18) + { + if(extra_size) + { + *extra_size = (p[17] << 8) | p[16]; + if(*extra_size + 18 > buffer_size) *extra_size = buffer_size - 18; + } + if(extra_offset) *extra_offset = 18; + } + + /* Skip the MPEGLAYER3WAVEFORMAT structure */ + if(waveformat_id == WAVE_FORMAT_MPEGLAYER3 && extra_size) + { + if(extra_offset) *extra_offset += *extra_size; + *extra_size = 0; + } + + format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + format->codec = fourcc; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +unsigned int vc_container_es_format_to_bitmapinfoheader(VC_CONTAINER_ES_FORMAT_T *format, + uint8_t *buffer, unsigned int buffer_size) +{ + uint32_t fourcc = codec_to_vfw_fourcc(format->codec); + uint32_t size = BITMAPINFOHEADER_SIZE_MAX + format->extradata_size; + + if(format->es_type != VC_CONTAINER_ES_TYPE_VIDEO || + fourcc == VC_CONTAINER_CODEC_UNKNOWN) return 0; + + if(!buffer) return size; + if(buffer_size < size) return 0; + + /* Build a bitmapinfoheader header */ + memset(buffer, 0, BITMAPINFOHEADER_SIZE_MAX); + buffer[0] = (size >> 0) & 0xFF; + buffer[1] = (size >> 8) & 0xFF; + buffer[2] = (size >> 16) & 0xFF; + buffer[3] = (size >> 24) & 0xFF; + buffer[4] = (format->type->video.width >> 0) & 0xFF; + buffer[5] = (format->type->video.width >> 8) & 0xFF; + buffer[6] = (format->type->video.width >> 16) & 0xFF; + buffer[7] = (format->type->video.width >> 24) & 0xFF; + buffer[8] = (format->type->video.height >> 0) & 0xFF; + buffer[9] = (format->type->video.height >> 8) & 0xFF; + buffer[10] = (format->type->video.height >> 16) & 0xFF; + buffer[11] = (format->type->video.height >> 24) & 0xFF; + memcpy(buffer + 16, &fourcc, 4); + memcpy(buffer + BITMAPINFOHEADER_SIZE_MAX, format->extradata, format->extradata_size); + return size; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_bitmapinfoheader_to_es_format(uint8_t *p, + unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, + VC_CONTAINER_ES_FORMAT_T *format) +{ + VC_CONTAINER_FOURCC_T fourcc; + + if(!p || buffer_size < BITMAPINFOHEADER_SIZE_MAX) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + /* size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; */ + format->type->video.width = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]; + format->type->video.height = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]; + memcpy(&fourcc, p + 16, 4); + + format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + format->codec = vfw_fourcc_to_codec(fourcc); + + /* If no mapping is found from vfw, try a more generic one */ + if (format->codec == fourcc && (fourcc = fourcc_to_codec(fourcc)) != VC_CONTAINER_CODEC_UNKNOWN) + format->codec = fourcc; + + if(extra_offset) *extra_offset = BITMAPINFOHEADER_SIZE_MAX; + if(extra_size) + { + if (buffer_size > BITMAPINFOHEADER_SIZE_MAX) + *extra_size = buffer_size - BITMAPINFOHEADER_SIZE_MAX; + else + *extra_size = 0; + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static struct { + VC_CONTAINER_METADATA_KEY_T key; + const char *name; +} meta_key_conv[] = +{ {VC_CONTAINER_METADATA_KEY_TITLE, "title"}, + {VC_CONTAINER_METADATA_KEY_ARTIST, "artist"}, + {VC_CONTAINER_METADATA_KEY_ALBUM, "album"}, + {VC_CONTAINER_METADATA_KEY_DESCRIPTION, "description"}, + {VC_CONTAINER_METADATA_KEY_YEAR, "year"}, + {VC_CONTAINER_METADATA_KEY_GENRE, "genre"}, + {VC_CONTAINER_METADATA_KEY_TRACK, "track"}, + {VC_CONTAINER_METADATA_KEY_LYRICS, "lyrics"}, + {VC_CONTAINER_METADATA_KEY_UNKNOWN, 0} }; + +/*****************************************************************************/ +const char *vc_container_metadata_id_to_string(VC_CONTAINER_METADATA_KEY_T key) +{ + int i; + for(i = 0; meta_key_conv[i].key != VC_CONTAINER_METADATA_KEY_UNKNOWN; i++ ) + if(meta_key_conv[i].key == key) break; + return meta_key_conv[i].name; +} + +/*****************************************************************************/ +int64_t vc_container_maths_gcd(int64_t a, int64_t b) +{ + while(b != 0) + { + int64_t t = b; + b = a % b; + a = t; + } + return a; +} + +/*****************************************************************************/ +void vc_container_maths_rational_simplify(uint32_t *num, uint32_t *den) +{ + int64_t div = vc_container_maths_gcd((int64_t)*num, (int64_t)*den); + if(div) + { + *num /= div; + *den /= div; + } +} diff --git a/containers/core/containers_utils.h b/containers/core/containers_utils.h new file mode 100755 index 0000000..22349ef --- /dev/null +++ b/containers/core/containers_utils.h @@ -0,0 +1,86 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_UTILS_H +#define VC_CONTAINERS_UTILS_H + +#include "containers/containers.h" +#include "containers/containers_codecs.h" +#include "containers/core/containers_waveformat.h" + +/***************************************************************************** + * Type definitions + *****************************************************************************/ + +/** Definition of the Global Unique Identifier type as used by some containers */ +typedef struct GUID_T +{ + uint32_t word0; + uint16_t short0; + uint16_t short1; + uint8_t bytes[8]; + +} GUID_T; + +VC_CONTAINER_ES_FORMAT_T *vc_container_format_create(unsigned int extradata_size); +void vc_container_format_delete(VC_CONTAINER_ES_FORMAT_T *); +VC_CONTAINER_STATUS_T vc_container_format_extradata_alloc( + VC_CONTAINER_ES_FORMAT_T *format, unsigned int size); +VC_CONTAINER_STATUS_T vc_container_format_copy( VC_CONTAINER_ES_FORMAT_T *p_out, + VC_CONTAINER_ES_FORMAT_T *p_in, + unsigned int extra_buffer_size ); +int utf8_from_charset(const char *charset, char *out, unsigned int out_size, + const void *in, unsigned int in_size); +const char *vc_container_metadata_id_to_string(VC_CONTAINER_METADATA_KEY_T key); + +unsigned int vc_container_es_format_to_waveformatex(VC_CONTAINER_ES_FORMAT_T *format, + uint8_t *buffer, unsigned int buffer_size); +unsigned int vc_container_es_format_to_bitmapinfoheader(VC_CONTAINER_ES_FORMAT_T *format, + uint8_t *buffer, unsigned int buffer_size); +VC_CONTAINER_STATUS_T vc_container_waveformatex_to_es_format(uint8_t *p, + unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, + VC_CONTAINER_ES_FORMAT_T *format); +VC_CONTAINER_STATUS_T vc_container_bitmapinfoheader_to_es_format(uint8_t *p, + unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size, + VC_CONTAINER_ES_FORMAT_T *format); + +/** Find the greatest common denominator of 2 numbers. + * + * @param a first number + * @param b second number + * + * @return greatest common denominator of a and b + */ +int64_t vc_container_maths_gcd(int64_t a, int64_t b); + +/** Reduce a rational number to it's simplest form. + * + * @param num Pointer to the numerator of the rational number to simplify + * @param den Pointer to the denominator of the rational number to simplify + */ +void vc_container_maths_rational_simplify(uint32_t *num, uint32_t *den); + +#endif /* VC_CONTAINERS_UTILS_H */ diff --git a/containers/core/containers_waveformat.h b/containers/core/containers_waveformat.h new file mode 100755 index 0000000..ade6b9e --- /dev/null +++ b/containers/core/containers_waveformat.h @@ -0,0 +1,180 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_WAVEFORMAT_H +#define VC_CONTAINERS_WAVEFORMAT_H + +/* WAVE form wFormatTag IDs */ +#define WAVE_FORMAT_UNKNOWN 0x0000 /* Microsoft Corporation */ +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 /* Microsoft Corporation */ +#define WAVE_FORMAT_VSELP 0x0004 /* Compaq Computer Corp. */ +#define WAVE_FORMAT_IBM_CVSD 0x0005 /* IBM Corporation */ +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +#define WAVE_FORMAT_DTS 0x0008 /* Microsoft Corporation */ +#define WAVE_FORMAT_DRM 0x0009 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO_VOICE 0x000A /* Microsoft Corporation */ +#define WAVE_FORMAT_OKI_ADPCM 0x0010 /* OKI */ +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +#define WAVE_FORMAT_IMA_ADPCM (WAVE_FORMAT_DVI_ADPCM) /* Intel Corporation */ +#define WAVE_FORMAT_MEDIASPACE_ADPCM 0x0012 /* Videologic */ +#define WAVE_FORMAT_SIERRA_ADPCM 0x0013 /* Sierra Semiconductor Corp */ +#define WAVE_FORMAT_G723_ADPCM 0x0014 /* Antex Electronics Corporation */ +#define WAVE_FORMAT_DIGISTD 0x0015 /* DSP Solutions, Inc. */ +#define WAVE_FORMAT_DIGIFIX 0x0016 /* DSP Solutions, Inc. */ +#define WAVE_FORMAT_DIALOGIC_OKI_ADPCM 0x0017 /* Dialogic Corporation */ +#define WAVE_FORMAT_MEDIAVISION_ADPCM 0x0018 /* Media Vision, Inc. */ +#define WAVE_FORMAT_CU_CODEC 0x0019 /* Hewlett-Packard Company */ +#define WAVE_FORMAT_YAMAHA_ADPCM 0x0020 /* Yamaha Corporation of America */ +#define WAVE_FORMAT_SONARC 0x0021 /* Speech Compression */ +#define WAVE_FORMAT_DSPGROUP_TRUESPEECH 0x0022 /* DSP Group, Inc */ +#define WAVE_FORMAT_ECHOSC1 0x0023 /* Echo Speech Corporation */ +#define WAVE_FORMAT_AUDIOFILE_AF36 0x0024 /* Virtual Music, Inc. */ +#define WAVE_FORMAT_APTX 0x0025 /* Audio Processing Technology */ +#define WAVE_FORMAT_AUDIOFILE_AF10 0x0026 /* Virtual Music, Inc. */ +#define WAVE_FORMAT_PROSODY_1612 0x0027 /* Aculab plc */ +#define WAVE_FORMAT_LRC 0x0028 /* Merging Technologies S.A. */ +#define WAVE_FORMAT_DOLBY_AC2 0x0030 /* Dolby Laboratories */ +#define WAVE_FORMAT_GSM610 0x0031 /* Microsoft Corporation */ +#define WAVE_FORMAT_MSNAUDIO 0x0032 /* Microsoft Corporation */ +#define WAVE_FORMAT_ANTEX_ADPCME 0x0033 /* Antex Electronics Corporation */ +#define WAVE_FORMAT_CONTROL_RES_VQLPC 0x0034 /* Control Resources Limited */ +#define WAVE_FORMAT_DIGIREAL 0x0035 /* DSP Solutions, Inc. */ +#define WAVE_FORMAT_DIGIADPCM 0x0036 /* DSP Solutions, Inc. */ +#define WAVE_FORMAT_CONTROL_RES_CR10 0x0037 /* Control Resources Limited */ +#define WAVE_FORMAT_NMS_VBXADPCM 0x0038 /* Natural MicroSystems */ +#define WAVE_FORMAT_CS_IMAADPCM 0x0039 /* Crystal Semiconductor IMA ADPCM */ +#define WAVE_FORMAT_ECHOSC3 0x003A /* Echo Speech Corporation */ +#define WAVE_FORMAT_ROCKWELL_ADPCM 0x003B /* Rockwell International */ +#define WAVE_FORMAT_ROCKWELL_DIGITALK 0x003C /* Rockwell International */ +#define WAVE_FORMAT_XEBEC 0x003D /* Xebec Multimedia Solutions Limited */ +#define WAVE_FORMAT_G721_ADPCM 0x0040 /* Antex Electronics Corporation */ +#define WAVE_FORMAT_G728_CELP 0x0041 /* Antex Electronics Corporation */ +#define WAVE_FORMAT_MSG723 0x0042 /* Microsoft Corporation */ +#define WAVE_FORMAT_PANASONIC_G726 0x0045 /* Not official Panasonic G.726 codec */ +#define WAVE_FORMAT_MPEG 0x0050 /* Microsoft Corporation */ +#define WAVE_FORMAT_RT24 0x0052 /* InSoft, Inc. */ +#define WAVE_FORMAT_PAC 0x0053 /* InSoft, Inc. */ +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +#define WAVE_FORMAT_LUCENT_G723 0x0059 /* Lucent Technologies */ +#define WAVE_FORMAT_CIRRUS 0x0060 /* Cirrus Logic */ +#define WAVE_FORMAT_ESPCM 0x0061 /* ESS Technology */ +#define WAVE_FORMAT_VOXWARE 0x0062 /* Voxware Inc */ +#define WAVE_FORMAT_CANOPUS_ATRAC 0x0063 /* Canopus, co., Ltd. */ +#define WAVE_FORMAT_G726_ADPCM 0x0064 /* APICOM */ +#define WAVE_FORMAT_G722_ADPCM 0x0065 /* APICOM */ +#define WAVE_FORMAT_DSAT_DISPLAY 0x0067 /* Microsoft Corporation */ +#define WAVE_FORMAT_VOXWARE_BYTE_ALIGNED 0x0069 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_AC8 0x0070 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_AC10 0x0071 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_AC16 0x0072 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_AC20 0x0073 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_RT24 0x0074 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_RT29 0x0075 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_RT29HW 0x0076 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_VR12 0x0077 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_VR18 0x0078 /* Voxware Inc */ +#define WAVE_FORMAT_VOXWARE_TQ40 0x0079 /* Voxware Inc */ +#define WAVE_FORMAT_SOFTSOUND 0x0080 /* Softsound, Ltd. */ +#define WAVE_FORMAT_VOXWARE_TQ60 0x0081 /* Voxware Inc */ +#define WAVE_FORMAT_MSRT24 0x0082 /* Microsoft Corporation */ +#define WAVE_FORMAT_G729A 0x0083 /* AT&T Labs, Inc. */ +#define WAVE_FORMAT_MVI_MVI2 0x0084 /* Motion Pixels */ +#define WAVE_FORMAT_DF_G726 0x0085 /* DataFusion Systems (Pty) (Ltd) */ +#define WAVE_FORMAT_DF_GSM610 0x0086 /* DataFusion Systems (Pty) (Ltd) */ +#define WAVE_FORMAT_ISIAUDIO 0x0088 /* Iterated Systems, Inc. */ +#define WAVE_FORMAT_ONLIVE 0x0089 /* OnLive! Technologies, Inc. */ +#define WAVE_FORMAT_SBC24 0x0091 /* Siemens Business Communications Sys */ +#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092 /* Sonic Foundry */ +#define WAVE_FORMAT_MEDIASONIC_G723 0x0093 /* MediaSonic */ +#define WAVE_FORMAT_PROSODY_8KBPS 0x0094 /* Aculab plc */ +#define WAVE_FORMAT_ZYXEL_ADPCM 0x0097 /* ZyXEL Communications, Inc. */ +#define WAVE_FORMAT_PHILIPS_LPCBB 0x0098 /* Philips Speech Processing */ +#define WAVE_FORMAT_PACKED 0x0099 /* Studer Professional Audio AG */ +#define WAVE_FORMAT_MALDEN_PHONYTALK 0x00A0 /* Malden Electronics Ltd. */ +#define WAVE_FORMAT_MP4A 0x00FF /* AAC */ +#define WAVE_FORMAT_RHETOREX_ADPCM 0x0100 /* Rhetorex Inc. */ +#define WAVE_FORMAT_IRAT 0x0101 /* BeCubed Software Inc. */ +#define WAVE_FORMAT_VIVO_G723 0x0111 /* Vivo Software */ +#define WAVE_FORMAT_VIVO_SIREN 0x0112 /* Vivo Software */ +#define WAVE_FORMAT_DIGITAL_G723 0x0123 /* Digital Equipment Corporation */ +#define WAVE_FORMAT_SANYO_LD_ADPCM 0x0125 /* Sanyo Electric Co., Ltd. */ +#define WAVE_FORMAT_SIPROLAB_ACEPLNET 0x0130 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_ACELP4800 0x0131 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_ACELP8V3 0x0132 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_G729 0x0133 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_G729A 0x0134 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_KELVIN 0x0135 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_G726ADPCM 0x0140 /* Dictaphone Corporation */ +#define WAVE_FORMAT_QUALCOMM_PUREVOICE 0x0150 /* Qualcomm, Inc. */ +#define WAVE_FORMAT_QUALCOMM_HALFRATE 0x0151 /* Qualcomm, Inc. */ +#define WAVE_FORMAT_TUBGSM 0x0155 /* Ring Zero Systems, Inc. */ +#define WAVE_FORMAT_WMAUDIO1 0x0160 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO2 0x0161 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIOPRO 0x0162 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO_LOSSLESS 0x0163 /* Microsoft Corporation */ +#define WAVE_FORMAT_UNISYS_NAP_ADPCM 0x0170 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_ULAW 0x0171 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_ALAW 0x0172 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_16K 0x0173 /* Unisys Corp. */ +#define WAVE_FORMAT_CREATIVE_ADPCM 0x0200 /* Creative Labs, Inc */ +#define WAVE_FORMAT_CREATIVE_FASTSPEECH8 0x0202 /* Creative Labs, Inc */ +#define WAVE_FORMAT_CREATIVE_FASTSPEECH10 0x0203 /* Creative Labs, Inc */ +#define WAVE_FORMAT_UHER_ADPCM 0x0210 /* UHER informatic GmbH */ +#define WAVE_FORMAT_QUARTERDECK 0x0220 /* Quarterdeck Corporation */ +#define WAVE_FORMAT_ILINK_VC 0x0230 /* I-link Worldwide */ +#define WAVE_FORMAT_RAW_SPORT 0x0240 /* Aureal Semiconductor */ +#define WAVE_FORMAT_ESST_AC3 0x0241 /* ESS Technology, Inc. */ +#define WAVE_FORMAT_IPI_HSX 0x0250 /* Interactive Products, Inc. */ +#define WAVE_FORMAT_IPI_RPELP 0x0251 /* Interactive Products, Inc. */ +#define WAVE_FORMAT_CS2 0x0260 /* Consistent Software */ +#define WAVE_FORMAT_SONY_SCX 0x0270 /* Sony Corp. */ +#define WAVE_FORMAT_FM_TOWNS_SND 0x0300 /* Fujitsu Corp. */ +#define WAVE_FORMAT_BTV_DIGITAL 0x0400 /* Brooktree Corporation */ +#define WAVE_FORMAT_QDESIGN_MUSIC 0x0450 /* QDesign Corporation */ +#define WAVE_FORMAT_VME_VMPCM 0x0680 /* AT&T Labs, Inc. */ +#define WAVE_FORMAT_TPC 0x0681 /* AT&T Labs, Inc. */ +#define WAVE_FORMAT_OLIGSM 0x1000 /* Ing C. Olivetti & C., S.p.A. */ +#define WAVE_FORMAT_OLIADPCM 0x1001 /* Ing C. Olivetti & C., S.p.A. */ +#define WAVE_FORMAT_OLICELP 0x1002 /* Ing C. Olivetti & C., S.p.A. */ +#define WAVE_FORMAT_OLISBC 0x1003 /* Ing C. Olivetti & C., S.p.A. */ +#define WAVE_FORMAT_OLIOPR 0x1004 /* Ing C. Olivetti & C., S.p.A. */ +#define WAVE_FORMAT_LH_CODEC 0x1100 /* Lernout & Hauspie */ +#define WAVE_FORMAT_NORRIS 0x1400 /* Norris Communications, Inc. */ +#define WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS 0x1500 /* AT&T Labs, Inc. */ +#define WAVE_FORMAT_DVM 0x2000 /* FAST Multimedia AG */ +#define WAVE_FORMAT_AAC 0x706D /* AAC */ + +#if !defined(WAVE_FORMAT_EXTENSIBLE) +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE /* Microsoft */ +#endif // !defined(WAVE_FORMAT_EXTENSIBLE) + +#if !defined(WAVE_FORMAT_PCM) +#define WAVE_FORMAT_PCM 0x0001 +#endif // !defined(WAVE_FORMAT_PCM) + +#endif /* VC_CONTAINERS_WAVEFORMAT_H */ diff --git a/containers/core/containers_writer_utils.c b/containers/core/containers_writer_utils.c new file mode 100755 index 0000000..583ad93 --- /dev/null +++ b/containers/core/containers_writer_utils.c @@ -0,0 +1,127 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_private.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_writer_utils.h" +#include "vcos.h" + +#include + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T vc_container_writer_extraio_create(VC_CONTAINER_T *context, const char *uri, + VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PARAM_UNUSED(context); + + extraio->io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_WRITE, &status ); + extraio->refcount = 0; + extraio->temp = 0; + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_null(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + return vc_container_writer_extraio_create(context, "null://", extraio); +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_temp(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + unsigned int length = strlen(context->priv->io->uri) + 5; + char *uri = malloc(length); + if(!uri) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + snprintf(uri, length, "%s.tmp", context->priv->io->uri); + status = vc_container_writer_extraio_create(context, uri, extraio); + free(uri); + extraio->temp = true; + + if(status == VC_CONTAINER_SUCCESS && !context->priv->tmp_io) + context->priv->tmp_io = extraio->io; + + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_writer_extraio_delete(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + VC_CONTAINER_STATUS_T status; + char *uri = extraio->temp ? vcos_strdup(extraio->io->uri) : 0; + + while(extraio->refcount) vc_container_writer_extraio_disable(context, extraio); + status = vc_container_io_close( extraio->io ); + + /* coverity[check_return] On failure the worst case is a file or directory is not removed */ + if(uri) remove(uri); + if(uri) free(uri); + + if(context->priv->tmp_io == extraio->io) + context->priv->tmp_io = 0; + + return status; +} + +/*****************************************************************************/ +int64_t vc_container_writer_extraio_enable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + VC_CONTAINER_IO_T *tmp; + + if(!extraio->refcount) + { + vc_container_io_seek(extraio->io, INT64_C(0)); + tmp = context->priv->io; + context->priv->io = extraio->io; + extraio->io = tmp; + } + return extraio->refcount++; +} + +/*****************************************************************************/ +int64_t vc_container_writer_extraio_disable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio) +{ + VC_CONTAINER_IO_T *tmp; + + if(extraio->refcount) + { + extraio->refcount--; + if(!extraio->refcount) + { + tmp = context->priv->io; + context->priv->io = extraio->io; + extraio->io = tmp; + } + } + return extraio->refcount; +} diff --git a/containers/core/containers_writer_utils.h b/containers/core/containers_writer_utils.h new file mode 100755 index 0000000..e2ccf9e --- /dev/null +++ b/containers/core/containers_writer_utils.h @@ -0,0 +1,96 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_CONTAINERS_WRITER_UTILS_H +#define VC_CONTAINERS_WRITER_UTILS_H + +/** \file containers_writer_utils.h + * Helper functions and macros for container writers + */ + +#include "containers/containers.h" +#include "containers/containers_codecs.h" +#include "containers/core/containers_io_helpers.h" + +/***************************************************************************** + * Helper inline functions to write format specific structus to an i/o stream + *****************************************************************************/ + +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_write_waveformatex( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_ES_FORMAT_T *format) +{ + /* Write waveformatex structure */ + WRITE_U16(p_ctx, codec_to_waveformat(format->codec), "Codec ID"); + WRITE_U16(p_ctx, format->type->audio.channels, "Number of Channels"); + WRITE_U32(p_ctx, format->type->audio.sample_rate, "Samples per Second"); + WRITE_U32(p_ctx, format->bitrate >> 3, "Average Number of Bytes Per Second"); + WRITE_U16(p_ctx, format->type->audio.block_align, "Block Alignment"); + WRITE_U16(p_ctx, format->type->audio.bits_per_sample, "Bits Per Sample"); + WRITE_U16(p_ctx, format->extradata_size, "Codec Specific Data Size"); + WRITE_BYTES(p_ctx, format->extradata, format->extradata_size); + + return STREAM_STATUS(p_ctx); +} + +STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_write_bitmapinfoheader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_ES_FORMAT_T *format) +{ + VC_CONTAINER_FOURCC_T fourcc; + + /* Write bitmapinfoheader structure */ + WRITE_U32(p_ctx, 40, "Format Data Size"); + WRITE_U32(p_ctx, format->type->video.width, "Image Width"); + WRITE_U32(p_ctx, format->type->video.height, "Image Height"); + WRITE_U16(p_ctx, 0, "Reserved"); + WRITE_U16(p_ctx, 0, "Bits Per Pixel Count"); + fourcc = codec_to_vfw_fourcc(format->codec); + WRITE_BYTES(p_ctx, (char *)&fourcc, 4); /* Compression ID */ + LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc); + WRITE_U32(p_ctx, 0, "Image Size"); + WRITE_U32(p_ctx, 0, "Horizontal Pixels Per Meter"); + WRITE_U32(p_ctx, 0, "Vertical Pixels Per Meter"); + WRITE_U32(p_ctx, 0, "Colors Used Count"); + WRITE_U32(p_ctx, 0, "Important Colors Count"); + + WRITE_BYTES(p_ctx, format->extradata, format->extradata_size); + + return STREAM_STATUS(p_ctx); +} + +/* Helper functions to create and use extra i/o */ +typedef struct VC_CONTAINER_WRITER_EXTRAIO_T { + VC_CONTAINER_IO_T *io; + unsigned int refcount; + bool temp; +} VC_CONTAINER_WRITER_EXTRAIO_T; + +VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_null(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); +VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_temp(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); +VC_CONTAINER_STATUS_T vc_container_writer_extraio_delete(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); +int64_t vc_container_writer_extraio_enable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); +int64_t vc_container_writer_extraio_disable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null); + +#endif /* VC_CONTAINERS_WRITER_UTILS_H */ diff --git a/containers/core/packetizers.c b/containers/core/packetizers.c new file mode 100755 index 0000000..abfb2d7 --- /dev/null +++ b/containers/core/packetizers.c @@ -0,0 +1,233 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/packetizers.h" +#include "containers/core/packetizers_private.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" + +/** List of registered packetizers. */ +static VC_PACKETIZER_REGISTRY_ENTRY_T *registry; + +/*****************************************************************************/ +void vc_packetizer_register(VC_PACKETIZER_REGISTRY_ENTRY_T *entry) +{ + LOG_DEBUG(0, "registering packetizer %s", entry->name); + entry->next = registry; + registry = entry; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T vc_packetizer_load(VC_PACKETIZER_T *p_ctx) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + VC_PACKETIZER_REGISTRY_ENTRY_T *entry; + + /* Try all the packetizers until we find the right one */ + for (entry = registry; entry; entry = entry->next) + { + status = entry->open(p_ctx); + if(status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) + break; + } + + return status; +} + +/*****************************************************************************/ +static void vc_packetizer_unload(VC_PACKETIZER_T *p_ctx) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); +} + +/*****************************************************************************/ +VC_PACKETIZER_T *vc_packetizer_open( VC_CONTAINER_ES_FORMAT_T *in, + VC_CONTAINER_FOURCC_T out_variant, VC_CONTAINER_STATUS_T *p_status ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_PACKETIZER_T *p_ctx = 0; + + /* Allocate our context before trying out the different packetizers */ + p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv)); + if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv)); + p_ctx->priv = (VC_PACKETIZER_PRIVATE_T *)(p_ctx + 1); + bytestream_init( &p_ctx->priv->stream ); + + p_ctx->in = vc_container_format_create(in->extradata_size); + if(!p_ctx->in) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + p_ctx->out = vc_container_format_create(in->extradata_size); + if(!p_ctx->out) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + vc_container_format_copy( p_ctx->in, in, in->extradata_size ); + p_ctx->in->extradata_size = 0; + vc_container_format_copy( p_ctx->out, p_ctx->in, in->extradata_size ); + p_ctx->in->extradata_size = in->extradata_size; + p_ctx->out->extradata = p_ctx->in->extradata; + p_ctx->out->extradata_size = p_ctx->in->extradata_size; + p_ctx->out->codec_variant = out_variant; + + vc_container_time_init(&p_ctx->priv->time, 1000000); + + status = vc_packetizer_load(p_ctx); + if(status != VC_CONTAINER_SUCCESS) + goto error; + + end: + if(p_status) *p_status = status; + return p_ctx; + + error: + if(p_ctx) vc_packetizer_close(p_ctx); + p_ctx = NULL; + goto end; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_packetizer_close( VC_PACKETIZER_T *p_ctx ) +{ + VC_CONTAINER_BYTESTREAM_T *stream; + VC_CONTAINER_PACKET_T *packet, *next; + + if(!p_ctx) return VC_CONTAINER_SUCCESS; + + stream = &p_ctx->priv->stream; + + if(p_ctx->in) vc_container_format_delete(p_ctx->in); + if(p_ctx->out) vc_container_format_delete(p_ctx->out); + if(p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx); + if(p_ctx->priv->module_handle) vc_packetizer_unload(p_ctx); + + /* Free the bytestream */ + for(packet = stream->first; packet; packet = next) + { + next = packet->next; + if(packet->framework_data) free(packet); + } + + free(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_packetizer_push( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *in) +{ + /* Do some sanity checking on packet ? */ + + in->framework_data = 0; + bytestream_push(&p_ctx->priv->stream, in); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_packetizer_pop( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T **in, VC_PACKETIZER_FLAGS_T flags) +{ + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + VC_CONTAINER_PACKET_T *packet, *new, **prev; + + /* Release the packets which have been read */ + while((*in = bytestream_pop(stream)) != NULL) + { + if(*in && (*in)->framework_data) + { + free(*in); + continue; + } + + if(*in) + return VC_CONTAINER_SUCCESS; + } + + if(!(flags & VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT)) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + + /* Look for the 1st non-framework packet */ + for (packet = stream->first, prev = &stream->first; + packet && packet->framework_data; prev = &packet->next, packet = packet->next); + + if (!packet || (packet && packet->framework_data)) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + + /* We'll currently alloc an internal packet for each packet the client forcefully releases. + * We could probably do something a bit more clever than that though. */ + /* Replace the packet with a newly allocated one */ + new = malloc(sizeof(*packet) + packet->size); + if(!new) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + *new = *packet; + new->framework_data = new; + if(!new->next) + stream->last = &new->next; + if(stream->current == packet) + stream->current = new; + *prev = new; + new->data = (uint8_t *)&new[1]; + memcpy(new->data, packet->data, packet->size); + *in = packet; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_packetizer_read( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, VC_PACKETIZER_FLAGS_T flags) +{ + if(!packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + if(!packet && (flags & VC_CONTAINER_READ_FLAG_INFO)) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + if(packet && !packet->data && + (!(flags & VC_CONTAINER_READ_FLAG_INFO) && + !(flags & VC_CONTAINER_READ_FLAG_SKIP))) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + /* Always having a packet structure to work with simplifies things */ + if(!packet) + packet = &p_ctx->priv->packet; + + return p_ctx->priv->pf_packetize(p_ctx, packet, flags); +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_packetizer_reset( VC_PACKETIZER_T *p_ctx ) +{ + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + + bytestream_skip( stream, stream->bytes - stream->current_offset - stream->offset ); + + if (p_ctx->priv->pf_reset) + return p_ctx->priv->pf_reset(p_ctx); + else + return VC_CONTAINER_SUCCESS; +} diff --git a/containers/core/packetizers_private.h b/containers/core/packetizers_private.h new file mode 100755 index 0000000..ad5f8f5 --- /dev/null +++ b/containers/core/packetizers_private.h @@ -0,0 +1,111 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_PACKETIZERS_PRIVATE_H +#define VC_PACKETIZERS_PRIVATE_H + +/** \file + * Private interface for packetizers + */ + +#include +#include "containers/packetizers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_bytestream.h" +#include "containers/core/containers_time.h" + +/** \defgroup VcPacketizerModuleApi Packetizer Module API + * Private interface for modules implementing packetizers */ +/* @{ */ + +/** Context private to the packetizer instance. This private context is used to + * store data which shouldn't be exported by the public API. */ +typedef struct VC_PACKETIZER_PRIVATE_T +{ + /** Pointer to the private data of the packetizer module in use */ + struct VC_PACKETIZER_MODULE_T *module; + + /** Bytestream abstraction layer */ + struct VC_CONTAINER_BYTESTREAM_T stream; + + /** Current stream time */ + VC_CONTAINER_TIME_T time; + + /** Packetize the bytestream. + * + * \param context Pointer to the context of the instance of the packetizer + * \param out Pointer to the output packet structure which needs to be filled + * \param flags Miscellaneous flags controlling the packetizing + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_packetize)( VC_PACKETIZER_T *context, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags ); + + /** Reset packetizer state. + * + * \param context Pointer to the context of the instance of the packetizer + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_reset)( VC_PACKETIZER_T *context ); + + /** Closes a packetizer module. + * + * \param context Pointer to the context of the instance to close + * \return the status of the operation + */ + VC_CONTAINER_STATUS_T (*pf_close)( struct VC_PACKETIZER_T *context ); + + /** Pointer to the packetizer module code and symbols*/ + void *module_handle; + + /** Temporary packet structure used when the caller does not provide one */ + VC_CONTAINER_PACKET_T packet; + +} VC_PACKETIZER_PRIVATE_T; + +/** Structure used by packetizers to register themselves with the core. */ +typedef struct VC_PACKETIZER_REGISTRY_ENTRY_T +{ + struct VC_PACKETIZER_REGISTRY_ENTRY_T *next; /**< To link entries together */ + const char *name; /**< Name of the packetizer */ + VC_CONTAINER_STATUS_T (*open)( VC_PACKETIZER_T * ); /**< Called to open packetizer */ +} VC_PACKETIZER_REGISTRY_ENTRY_T; + +/** Register a packetizer with the core. + * + * \param entry Entry to register with the core + */ +void vc_packetizer_register(VC_PACKETIZER_REGISTRY_ENTRY_T *entry); + +/** Utility macro used to register a packetizer with the core */ +#define VC_PACKETIZER_REGISTER(func, name) \ +VC_CONTAINER_CONSTRUCTOR(func##_register); \ +static VC_PACKETIZER_REGISTRY_ENTRY_T registry_entry = {0, name, func}; \ +void func##_register(void) { vc_packetizer_register(®istry_entry); } + +/* @} */ + +#endif /* VC_PACKETIZERS_PRIVATE_H */ diff --git a/containers/dummy/CMakeLists.txt b/containers/dummy/CMakeLists.txt new file mode 100755 index 0000000..4643438 --- /dev/null +++ b/containers/dummy/CMakeLists.txt @@ -0,0 +1,12 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(writer_dummy ${LIBRARY_TYPE} dummy_writer.c) + +target_link_libraries(writer_dummy containers) + +install(TARGETS writer_dummy DESTINATION ${VMCS_PLUGIN_DIR}) diff --git a/containers/dummy/dummy_writer.c b/containers/dummy/dummy_writer.c new file mode 100755 index 0000000..0430b98 --- /dev/null +++ b/containers/dummy/dummy_writer.c @@ -0,0 +1,137 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track[2]; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T dummy_writer_open( VC_CONTAINER_T * ); + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T dummy_writer_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T dummy_writer_write( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(packet); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T dummy_writer_control( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_TRACK_T *track; + VC_CONTAINER_PARAM_UNUSED(args); + + switch(operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + if(p_ctx->tracks_num >= 2) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + /* Allocate and initialise track data */ + p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, 0); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + return VC_CONTAINER_SUCCESS; + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T dummy_writer_open( VC_CONTAINER_T *p_ctx ) +{ + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "dummy")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->track; + + p_ctx->capabilities |= VC_CONTAINER_CAPS_DYNAMIC_TRACK_ADD; + + p_ctx->priv->pf_close = dummy_writer_close; + p_ctx->priv->pf_write = dummy_writer_write; + p_ctx->priv->pf_control = dummy_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "dummy: error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open dummy_writer_open +#endif diff --git a/containers/flash/CMakeLists.txt b/containers/flash/CMakeLists.txt new file mode 100755 index 0000000..bf53935 --- /dev/null +++ b/containers/flash/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_flv ${LIBRARY_TYPE} flv_reader.c) + +target_link_libraries(reader_flv containers) + +install(TARGETS reader_flv DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/flash/flv_reader.c b/containers/flash/flv_reader.c new file mode 100755 index 0000000..6e15e6b --- /dev/null +++ b/containers/flash/flv_reader.c @@ -0,0 +1,1174 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +/* Work-around for MSVC debugger issue */ +#define VC_CONTAINER_MODULE_T VC_CONTAINER_MODULE_FLV_READER_T +#define VC_CONTAINER_TRACK_MODULE_T VC_CONTAINER_TRACK_MODULE_FLV_READER_T + +//#define ENABLE_FLV_EXTRA_LOGGING +#define CONTAINER_IS_BIG_ENDIAN +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_index.h" +#include "containers/core/containers_logging.h" +#undef CONTAINER_HELPER_LOG_INDENT +#define CONTAINER_HELPER_LOG_INDENT(a) 0 + +VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T *p_ctx ); + +/****************************************************************************** +Defines. +******************************************************************************/ +#define FLV_TRACKS_MAX 2 + +#define FLV_TAG_TYPE_AUDIO 8 +#define FLV_TAG_TYPE_VIDEO 9 +#define FLV_TAG_TYPE_METADATA 18 +#define FLV_TAG_HEADER_SIZE 15 + +#define FLV_SCRIPT_DATA_TYPE_NUMBER 0 +#define FLV_SCRIPT_DATA_TYPE_BOOL 1 +#define FLV_SCRIPT_DATA_TYPE_STRING 2 +#define FLV_SCRIPT_DATA_TYPE_ECMA 8 +#define FLV_SCRIPT_DATA_TYPE_LONGSTRING 12 + +#define FLV_FLAG_DISCARD 1 +#define FLV_FLAG_KEYFRAME 2 +#define FLV_FLAG_INTERFRAME 4 + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef struct +{ + VC_CONTAINER_STATUS_T status; + + int64_t tag_position; /* position of the current tag we're reading */ + int64_t data_position; /* position to the start of the data within the tag */ + int data_offset; /* current position inside the tag's data */ + int data_size; /* size of the data from the current tag */ + int tag_prev_size; /* size of the previous tag in the stream */ + int flags; /* flags for the current tag */ + uint32_t timestamp; /* timestamp for the current tag */ + uint32_t track; /* track the current tag belongs to */ + VC_CONTAINER_INDEX_T *index; /* index of key frames */ + +} FLV_READER_STATE_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + FLV_READER_STATE_T *state; + FLV_READER_STATE_T track_state; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *tracks[FLV_TRACKS_MAX]; + + int64_t data_offset; /*< offset to the first FLV tag in the stream */ + + FLV_READER_STATE_T state; /*< global state of the reader */ + + int audio_track; + int video_track; + + uint32_t meta_videodatarate; + uint32_t meta_audiodatarate; + float meta_framerate; + uint32_t meta_width; + uint32_t meta_height; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Static functions within this file. +******************************************************************************/ +/** Reads an FLV tag header + * + * @param p_ctx pointer to our context + * @param[out] p_prev_size size of the previous tag + * @param[out] p_type type of the tag + * @param[out] p_type size of the tag + * @param[out] p_type timestamp for the tag + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_read_tag_header(VC_CONTAINER_T *p_ctx, int *p_prev_size, + int *p_type, int *p_size, uint32_t *p_timestamp) +{ + int prev_size, type, size; + uint32_t timestamp; + + prev_size = READ_U32(p_ctx, "PreviousTagSize"); + type = READ_U8(p_ctx, "TagType"); + size = READ_U24(p_ctx, "DataSize"); + timestamp = READ_U24(p_ctx, "Timestamp"); + timestamp |= (READ_U8(p_ctx, "TimestampExtended") << 24); + SKIP_U24(p_ctx, "StreamID"); + + if(p_prev_size) *p_prev_size = prev_size + 4; + if(p_type) *p_type = type; + if(p_size) *p_size = size; + if(p_timestamp) *p_timestamp = timestamp; + + return STREAM_STATUS(p_ctx); +} + +/** Reads an FLV video data header. + * This contains the codec id and the current frame type. + * + * @param p_ctx pointer to our context + * @param[out] codec video codec + * @param[out] frame_type type of the current frame + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_read_videodata_header(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_FOURCC_T *codec, int *frame_type) +{ + uint8_t header = READ_U8(p_ctx, "FrameType/CodecID"); + + if(frame_type) + *frame_type = (header >> 4) == 1 ? FLV_FLAG_KEYFRAME : + (header >> 4) == 3 ? FLV_FLAG_INTERFRAME : 0; + + switch(header &0xF) + { + case 2: *codec = VC_CONTAINER_CODEC_SPARK; break; + case 3: *codec = VC_FOURCC('s','c','r','1'); break; /* screen video */ + case 4: *codec = VC_CONTAINER_CODEC_VP6; break; + case 5: *codec = VC_FOURCC('v','p','6','a'); break; /* vp6 alpha */ + case 6: *codec = VC_FOURCC('s','c','r','2'); break; /* screen video 2 */ + case 7: *codec = VC_CONTAINER_CODEC_H264; break; + default: *codec = 0; break; + } + + return STREAM_STATUS(p_ctx); +} + +/** Get the properties of a video frame + * This is only really useful at setup time when trying to detect + * the type of content we are dealing with. + * This will try to get some of the properties of the video stream + * as well as codec configuration data if there is any. + * + * @param p_ctx pointer to our context + * @param track track number this data/tag belongs to + * @param size size of the data we are parsing + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_read_videodata_properties(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, int size) +{ + VC_CONTAINER_STATUS_T status; + int width = 0, height = 0; + + if(track->format->codec == VC_CONTAINER_CODEC_VP6 || + track->format->codec == VC_FOURCC('v','p','6','a')) + { + /* Just extract the width / height */ + uint8_t data = _READ_U8(p_ctx); + _SKIP_U16(p_ctx); + height = _READ_U8(p_ctx) * 16; + width = _READ_U8(p_ctx) * 16; + width -= data >> 4; + height -= data & 0xf; + } + else if(track->format->codec == VC_CONTAINER_CODEC_H264) + { + uint8_t type = _READ_U8(p_ctx); size--; + if(type || size <= 8) return VC_CONTAINER_ERROR_CORRUPTED; + _SKIP_U24(p_ctx); size-=3; + + track->format->codec_variant = VC_FOURCC('a','v','c','C'); + status = vc_container_track_allocate_extradata(p_ctx, track, size); + if(status != VC_CONTAINER_SUCCESS) return status; + track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); + } + + track->format->type->video.width = width; + track->format->type->video.height = height; + + return STREAM_STATUS(p_ctx); +} + +/** Reads an FLV audio data header. + * This contains the codec id, samplerate, number of channels and bitrate. + * + * @param p_ctx pointer to our context + * @param[out] codec audio codec + * @param[out] p_samplerate audio sampling rate + * @param[out] p_channels number of audio channels + * @param[out] p_bps bits per sample + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_read_audiodata_header(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_FOURCC_T *codec, int *p_samplerate, int *p_channels, int *p_bps) +{ + int samplerate, channels, bps; + uint8_t header = _READ_U8(p_ctx); + + switch((header >> 2) & 0x3) + { + case 0: samplerate = 5512; break; + case 1: samplerate = 11025; break; + case 2: samplerate = 22050; break; + default: + case 3: samplerate = 44100; break; + } + + channels = 1 << (header & 1); + bps = 8 << ((header >> 1) & 1); + + switch(header >> 4) + { + case 0: *codec = bps == 8 ? VC_CONTAINER_CODEC_PCM_UNSIGNED : VC_CONTAINER_CODEC_PCM_SIGNED; break; + case 1: *codec = VC_CONTAINER_CODEC_ADPCM_SWF; break; + case 2: *codec = VC_CONTAINER_CODEC_MPGA; break; + case 3: *codec = bps == 8 ? VC_CONTAINER_CODEC_PCM_UNSIGNED : VC_CONTAINER_CODEC_PCM_SIGNED_LE; break; + case 4: *codec = VC_CONTAINER_CODEC_NELLYMOSER; samplerate = 8000; channels = 1; break; + case 5: *codec = VC_CONTAINER_CODEC_NELLYMOSER; samplerate = 16000; channels = 1; break; + case 6: *codec = VC_CONTAINER_CODEC_NELLYMOSER; channels = 1; break; + case 7: *codec = VC_CONTAINER_CODEC_ALAW; break; + case 8: *codec = VC_CONTAINER_CODEC_MULAW; break; + case 10: *codec = VC_CONTAINER_CODEC_MP4A; samplerate = 44100; channels = 2; break; + case 11: *codec = VC_CONTAINER_CODEC_SPEEX; break; + case 14: *codec = VC_CONTAINER_CODEC_MPGA; samplerate = 8000; break; + default: *codec = 0; break; + } + + if(p_samplerate) *p_samplerate = samplerate; + if(p_channels) *p_channels = channels; + if(p_bps) *p_bps = bps; + + return STREAM_STATUS(p_ctx); +} + +/** Get the properties of an audio frame + * This is only really useful at setup time when trying to detect + * the type of content we are dealing with. + * This will try to get some of the properties of the audio stream + * as well as codec configuration data if there is any. + * + * @param p_ctx pointer to our context + * @param track track number this data/tag belongs to + * @param size size of the data we are parsing + * @param samplerate sampling rate of the audio data + * @param channels number of channels of the audio data + * @param bps bits per sample + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_read_audiodata_properties(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, int size, int samplerate, int channels, int bps) +{ + static const int aac_freq[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, + 22050, 16000, 12000, 11025, 8000, 7350}; + VC_CONTAINER_STATUS_T status; + + if(track->format->codec == VC_CONTAINER_CODEC_MP4A) + { + uint8_t *p_data, data = _READ_U8(p_ctx); + size--; + if(data || size <= 0) return VC_CONTAINER_ERROR_FAILED; + + /* Read the AudioSpecificConfig */ + status = vc_container_track_allocate_extradata(p_ctx, track, size); + if(status != VC_CONTAINER_SUCCESS) return status; + track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); + + if(track->format->extradata_size >= 2) + { + p_data = track->format->extradata; + data = ((p_data[0] & 0x7) << 1) | (p_data[1] >> 7); + if(data >= countof(aac_freq)) + return VC_CONTAINER_ERROR_FAILED; + + samplerate = aac_freq[data]; + channels = (p_data[1] >> 3) & 0xf; + if(track->format->extradata_size >= 5 && data == 0xf) + { + samplerate = ((p_data[1] & 0x7f) << 17) | (p_data[2] << 9) | + (p_data[3] << 1) | (p_data[4] >> 7); + channels = (p_data[4] >> 3) & 0xf; + } + } + } + + track->format->type->audio.sample_rate = samplerate; + track->format->type->audio.channels = channels; + track->format->type->audio.bits_per_sample = bps; + + return STREAM_STATUS(p_ctx); +} + +/** Reads an FLV metadata tag. + * This contains metadata information about the stream. + * All the data we extract from this will be placed directly in the context. + * + * @param p_ctx pointer to our context + * @param size size of the tag + * @return FLV_FILE_NO_ERROR on success + */ +static int flv_read_metadata(VC_CONTAINER_T *p_ctx, int size) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; +#define MAX_METADATA_STRING_SIZE 25 + char psz_string[MAX_METADATA_STRING_SIZE+1]; + uint16_t length, num_values; + double f_value; + uint64_t u_value; + uint8_t type; + + /* We're looking for an onMetaData script */ + type = READ_U8(p_ctx, "Type"); size--; + if(type != FLV_SCRIPT_DATA_TYPE_STRING) return VC_CONTAINER_SUCCESS; + length = READ_U16(p_ctx, "StringLength"); size -= 2; + if(!length || length > size || length > MAX_METADATA_STRING_SIZE) return VC_CONTAINER_SUCCESS; + if(READ_BYTES(p_ctx, psz_string, length) != length) return VC_CONTAINER_SUCCESS; + psz_string[length] = 0; size -= length; + if(strcmp(psz_string, "onMetaData")) return VC_CONTAINER_SUCCESS; + if(size < 5) return VC_CONTAINER_SUCCESS; + type = READ_U8(p_ctx, "Type"); size--; + if(type != FLV_SCRIPT_DATA_TYPE_ECMA) return VC_CONTAINER_SUCCESS; + num_values = READ_U32(p_ctx, "ECMAArrayLength"); size -= 4; + + /* We found our script, now extract the metadata values */ + while(num_values-- && size >= 2) + { + uint16_t length = _READ_U16(p_ctx); size -= 2; + if(!length || length >= size || length > MAX_METADATA_STRING_SIZE) break; + if(READ_BYTES(p_ctx, psz_string, length) != length) break; + psz_string[length] = 0; size -= length; + type = _READ_U8(p_ctx); size--; + + switch(type) + { + case FLV_SCRIPT_DATA_TYPE_NUMBER: + /* We only cope with DOUBLE types*/ + if(size < 8) return VC_CONTAINER_SUCCESS; + + u_value = _READ_U64(p_ctx); size -= 8; + /* Convert value into a double */ + { + int64_t value = ((u_value & ((UINT64_C(1)<<52)-1)) + (UINT64_C(1)<<52)) * ((((int64_t)u_value)>>63)|1); + int exp = ((u_value>>52)&0x7FF)-1075 + 16; + if(exp >= 0) value <<= exp; + else value >>= -exp; + f_value = ((double)value) / (1 << 16); + } + + LOG_DEBUG(p_ctx, "metadata (%s=%i.%i)", psz_string, + ((int)(f_value*100))/100, ((int)(f_value*100))%100); + + if(!strcmp(psz_string, "duration")) + p_ctx->duration = (int64_t)(f_value*INT64_C(1000000)); + if(!strcmp(psz_string, "videodatarate")) + module->meta_videodatarate = (uint32_t)f_value; + if(!strcmp(psz_string, "width")) + module->meta_width = (uint32_t)f_value; + if(!strcmp(psz_string, "height")) + module->meta_height = (uint32_t)f_value; + if(!strcmp(psz_string, "framerate")) + module->meta_framerate = f_value; + if(!strcmp(psz_string, "audiodatarate")) + module->meta_audiodatarate = (uint32_t)f_value; + continue; + + /* We skip these */ + case FLV_SCRIPT_DATA_TYPE_BOOL: + if(size < 1) return VC_CONTAINER_SUCCESS; + u_value = _READ_U8(p_ctx); size -= 1; + LOG_DEBUG(p_ctx, "metadata (%s=%i)", psz_string, (int)u_value); + continue; + + case FLV_SCRIPT_DATA_TYPE_STRING: + if(size < 2) return VC_CONTAINER_SUCCESS; + length = _READ_U16(p_ctx); size -= 2; + if(length > size) return VC_CONTAINER_SUCCESS; + SKIP_BYTES(p_ctx, length); size -= length; + LOG_DEBUG(p_ctx, "metadata skipping (%s)", psz_string); + continue; + + /* We can't cope with anything else */ + default: + LOG_DEBUG(p_ctx, "unknown amf type (%s,%i)", psz_string, type); + return VC_CONTAINER_SUCCESS; + } + } + + return STREAM_STATUS(p_ctx); +} + +/** Reads an FLV frame header. + * This reads the current tag header and matches the contained frame + * with one of the tracks we have. If no match can be found, the frame is marked + * for discarding. The current read position will be updated to the start + * of the data (i.e. the frame) contained within the FLV tag. + * + * @param p_ctx pointer to our context + * @param[out] p_track track this frame belongs to + * @param[out] p_size size of the frame + * @param[out] p_timestamp timestamp of the frame + * @param[out] p_flags flags associated with the frame + * @param b_extra_check whether to perform extra sanity checking on the tag + * @return VC_CONTAINER_SUCCESS on success + */ +static int flv_read_frame_header(VC_CONTAINER_T *p_ctx, int *p_prev_size, + int *p_track, int *p_size, uint32_t *p_timestamp, int *p_flags, + int b_extra_check) +{ + int64_t position = STREAM_POSITION(p_ctx); + int type, size, flags = 0, frametype = 0; + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN; + unsigned int track = p_ctx->tracks_num; + uint32_t codec = 0; + + status = flv_read_tag_header(p_ctx, p_prev_size, &type, &size, p_timestamp); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return status; + if(position == STREAM_POSITION(p_ctx)) return VC_CONTAINER_ERROR_EOS; + if(type == 0) return VC_CONTAINER_ERROR_CORRUPTED; + + /* Sanity checking */ + if(b_extra_check && type != FLV_TAG_TYPE_AUDIO && + type != FLV_TAG_TYPE_VIDEO && type != FLV_TAG_TYPE_METADATA) + return VC_CONTAINER_ERROR_CORRUPTED; + + /* We're only interested in audio / video */ + if((type != FLV_TAG_TYPE_AUDIO && type != FLV_TAG_TYPE_VIDEO) || !size) + { + flags |= FLV_FLAG_DISCARD; + goto end; + } + + if(type == FLV_TAG_TYPE_AUDIO) + { + flv_read_audiodata_header(p_ctx, &codec, 0, 0, 0); + es_type = VC_CONTAINER_ES_TYPE_AUDIO; + } + else if(type == FLV_TAG_TYPE_VIDEO) + { + flv_read_videodata_header(p_ctx, &codec, &frametype); + es_type = VC_CONTAINER_ES_TYPE_VIDEO; + } + size--; + + /* Find which track this belongs to */ + for(track = 0; track < p_ctx->tracks_num; track++) + if(es_type == p_ctx->tracks[track]->format->es_type) break; + if(track == p_ctx->tracks_num) + flags |= FLV_FLAG_DISCARD; + + /* Sanity checking */ + if(b_extra_check && codec != p_ctx->tracks[track]->format->codec) + return VC_CONTAINER_ERROR_CORRUPTED; + + end: + // add to the index if we have one, and we're not discarding this frame. + // also require that we either have no video track or this is a video frame marked as a key frame. + if(p_ctx->priv->module->state.index && !(flags & FLV_FLAG_DISCARD) && + (p_ctx->priv->module->video_track < 0 || (es_type == VC_CONTAINER_ES_TYPE_VIDEO && (frametype & FLV_FLAG_KEYFRAME)))) + vc_container_index_add(p_ctx->priv->module->state.index, (int64_t) (*p_timestamp) * 1000LL, position); + + *p_flags = flags | frametype; + *p_size = size; + *p_track = track; + return VC_CONTAINER_SUCCESS; +} + +/** Validate the data contained within the frame and update the read + * position to the start of the frame data that we want to feed to the codec. + * + * Each codec is packed slightly differently so this function is necessary + * to prepare for reading the actual codec data. + * + * @param p_ctx pointer to our context + * @param track track this frame belongs to + * @param[in] p_size size of the frame + * @param[out] p_size updated size of the frame + * @param[in] p_timestamp timestamp for the frame + * @param[out] p_timestamp updated timestamp for the frame + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_validate_frame_data(VC_CONTAINER_T *p_ctx, + int track, int *p_size, uint32_t *p_timestamp) +{ + int32_t time_offset; + + if(track >= (int)p_ctx->tracks_num) + return *p_size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_CONTINUE; + + switch(p_ctx->tracks[track]->format->codec) + { + case VC_CONTAINER_CODEC_VP6: + if(*p_size < 1) return VC_CONTAINER_ERROR_CORRUPTED; + _READ_U8(p_ctx); *p_size -= 1; + break; + case VC_CONTAINER_CODEC_MP4A: + if(*p_size < 1) return VC_CONTAINER_ERROR_CORRUPTED; + *p_size -= 1; + if(_READ_U8(p_ctx)!=1) return VC_CONTAINER_ERROR_CONTINUE; /* empty frame*/ + break; + case VC_CONTAINER_CODEC_H264: + if(*p_size < 4) return VC_CONTAINER_ERROR_CORRUPTED; + *p_size -= 1; + if(_READ_U8(p_ctx)!=1) return VC_CONTAINER_ERROR_CONTINUE; /* empty frame*/ + time_offset = _READ_U24(p_ctx); + time_offset <<= 8; time_offset >>= 8; /* change to signed */ + *p_timestamp += time_offset; + *p_size -= 3; + break; + default: + break; + } + + return *p_size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_CONTINUE; +} + +/** Small utility function to update the reading position of a track + */ +static void flv_update_track_position(VC_CONTAINER_T *p_ctx, int track, + int64_t tag_position, int tag_prev_size, int64_t data_position, + int data_size, uint32_t timestamp, int flags) +{ + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; + track_module->state->tag_position = tag_position; + track_module->state->tag_prev_size = tag_prev_size; + track_module->state->data_position = data_position; + track_module->state->data_size = data_size; + track_module->state->data_offset = 0; + track_module->state->timestamp = timestamp; + track_module->state->flags = flags; + track_module->state->track = track; +} + +/** Utility function to find the next frame of a given track in the stream. + * + * This will basically walk through all the tags in the file until it + * finds a tag/frame which belongs to the given track. + * + * @param p_ctx pointer to our context + * @param track track wanted + * @param[out] p_size size of the frame + * @param[out] p_timestamp timestamp of the frame + * @param[out] p_flags flags associated with the frame + * @param b_keyframe whether we specifically want a keyframe or not + * @param b_extra_check whether to perform extra sanity checking on the tag + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_find_next_frame(VC_CONTAINER_T *p_ctx, int track, int *p_size, + uint32_t *p_timestamp, int *p_flags, int b_keyframe, int b_extra_check) +{ + int frame_track, prev_size, size, flags; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + FLV_READER_STATE_T *state = p_ctx->tracks[track]->priv->module->state; + uint32_t timestamp; + int64_t position; + VC_CONTAINER_PARAM_UNUSED(b_extra_check); + + /* Seek to the next tag in the stream or the current position + * if none of its data has been consumed */ + position = state->tag_position; + if(state->data_offset) + position = state->data_position + state->data_size; + status = SEEK(p_ctx, position); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* Look for the next frame we want */ + while (status == VC_CONTAINER_SUCCESS) + { + position = STREAM_POSITION(p_ctx); + status = flv_read_frame_header(p_ctx, &prev_size, &frame_track, + &size, ×tamp, &flags, 0); + if(status != VC_CONTAINER_SUCCESS) break; + + if(flags & FLV_FLAG_DISCARD) goto skip; + if(frame_track != track) goto skip; + + if(b_keyframe && p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && + !(flags & FLV_FLAG_KEYFRAME)) goto skip; + + if(flv_validate_frame_data(p_ctx, track, &size, ×tamp) != VC_CONTAINER_SUCCESS) + goto skip; + + /* We have what we need */ + flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), + size, timestamp, flags); + break; + + skip: + flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), + size, timestamp, 0); + state->data_offset = size; /* consume data */ + + if(SKIP_BYTES(p_ctx, size) != (size_t)size) status = STREAM_STATUS(p_ctx); + } + + if(!status) + { + if(p_size) *p_size = size; + if(p_timestamp) *p_timestamp = timestamp; + if(p_flags) *p_flags = flags; + } + + return status; +} + +/** Utility function to find the previous frame of a given track in the stream. + * + * This will basically walk back through all the tags in the file until it + * finds a tag/frame which belongs to the given track. + * + * @param p_ctx pointer to our context + * @param track track wanted + * @param[out] p_size size of the frame + * @param[out] p_timestamp timestamp of the frame + * @param[out] p_flags flags associated with the frame + * @param b_keyframe whether we specifically want a keyframe or not + * @param b_extra_check whether to perform extra sanity checking on the tag + * @return VC_CONTAINER_SUCCESS on success + */ +static VC_CONTAINER_STATUS_T flv_find_previous_frame(VC_CONTAINER_T *p_ctx, int track, int *p_size, + uint32_t *p_timestamp, int *p_flags, int b_keyframe, int b_extra_check) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + FLV_READER_STATE_T *state = p_ctx->tracks[track]->priv->module->state; + int frame_track, prev_size, size, flags; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t timestamp; + int64_t position; + + /* Look for the previous frame we want */ + while (status == VC_CONTAINER_SUCCESS) + { + /* Seek to the previous tag in the stream */ + position = state->tag_position - state->tag_prev_size; + if(position < module->data_offset) position = module->data_offset; + status = SEEK(p_ctx, position); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = flv_read_frame_header(p_ctx, &prev_size, &frame_track, + &size, ×tamp, &flags, 0); + if(status) break; + + if(flags & FLV_FLAG_DISCARD) goto skip; + if(frame_track != track) goto skip; + + if(b_keyframe && p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && + !(flags & FLV_FLAG_KEYFRAME)) goto skip; + + if(flv_validate_frame_data(p_ctx, track, &size, ×tamp) != VC_CONTAINER_SUCCESS) + goto skip; + + /* We have what we need */ + flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), + size, timestamp, flags); + break; + + skip: + if(position <= module->data_offset) + { + /* We're back at the beginning but we still want to return something */ + flv_update_track_position(p_ctx, track, (int64_t)module->data_offset, 0, + (int64_t)module->data_offset, 0, 0, 0); + return flv_find_next_frame(p_ctx, track, p_size, p_timestamp, p_flags, b_keyframe, b_extra_check); + } + flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx), + size, timestamp, 0); + state->data_offset = size; /* consume data */ + } + + if(!status) + { + if(p_size) *p_size = size; + if(p_timestamp) *p_timestamp = timestamp; + if(p_flags) *p_flags = flags; + } + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T flv_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + + if(module->state.index) + vc_container_index_free(module->state.index); + + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T flv_read_sample_header( VC_CONTAINER_T *p_ctx, + FLV_READER_STATE_T *state) +{ + int track, prev_size, size, flags; + uint32_t timestamp; + int64_t position; + + /* Check if we still have some data left to read from the current frame */ + if(state->data_offset < state->data_size) + return state->status; + + /* Read the next tag header */ + position = STREAM_POSITION(p_ctx); + state->status = flv_read_frame_header(p_ctx, &prev_size, &track, + &size, ×tamp, &flags, 0); + if(state->status != VC_CONTAINER_SUCCESS) + return state->status; + + state->status = flv_validate_frame_data(p_ctx, track, &size, ×tamp); + if(state->status == VC_CONTAINER_ERROR_CONTINUE) + { + /* Skip it */ + state->status = VC_CONTAINER_SUCCESS; + track = p_ctx->tracks_num; + } + if(state->status != VC_CONTAINER_SUCCESS) + return state->status; + + state->tag_position = position; + state->data_position = STREAM_POSITION(p_ctx); + state->data_size = size; + state->data_offset = 0; + state->flags = flags; + state->tag_prev_size = prev_size; + state->timestamp = timestamp; + state->track = track; + return state->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T flv_read_sample_data( VC_CONTAINER_T *p_ctx, + FLV_READER_STATE_T *state, uint8_t *data, unsigned int *data_size ) +{ + unsigned int size = state->data_size - state->data_offset; + + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + + if(data_size && *data_size < size) size = *data_size; + + if(!data) size = SKIP_BYTES(p_ctx, size); + else size = READ_BYTES(p_ctx, data, size); + state->data_offset += size; + + if(data_size) *data_size = size; + state->status = STREAM_STATUS(p_ctx); + + return state->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T flv_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + FLV_READER_STATE_T *state = &module->state; + unsigned int data_size; + + /* TODO: select right state */ + + status = flv_read_sample_header(p_ctx, state); + if(status != VC_CONTAINER_SUCCESS) return status; + +#ifdef ENABLE_FLV_EXTRA_LOGGING + LOG_DEBUG(p_ctx, "read_sample_header (%i,%i,%i/%i/%i/%i)", state->timestamp, state->flags, + (int)state->tag_position, (int)(state->data_position-state->tag_position), state->data_offset, state->data_size); +#endif + + if(state->track >= p_ctx->tracks_num || !p_ctx->tracks[state->track]->is_enabled) + { + /* Skip packet */ + status = flv_read_sample_data(p_ctx, state, 0, 0); + if(status != VC_CONTAINER_SUCCESS) return status; + return VC_CONTAINER_ERROR_CONTINUE; + } + + if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */ + return flv_read_sample_data(p_ctx, state, 0, 0); + + packet->dts = packet->pts = state->timestamp * (int64_t)1000; + packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + if(state->flags & FLV_FLAG_KEYFRAME) packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + if(!state->data_offset) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + packet->track = state->track; + + // The frame size is all the data + packet->frame_size = state->data_size; + + // the size is what's left + packet->size = state->data_size - state->data_offset; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + return flv_read_sample_data(p_ctx, state, 0, 0); + else if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + data_size = packet->buffer_size; + status = flv_read_sample_data(p_ctx, state, packet->data, &data_size); + if(status != VC_CONTAINER_SUCCESS) + { + /* FIXME */ + return status; + } + + packet->size = data_size; + if(state->data_offset != state->data_size) + packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T flv_reader_seek(VC_CONTAINER_T *p_ctx, + int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + FLV_READER_STATE_T last_state = {0}; + FLV_READER_STATE_T *state; + uint32_t time = (*offset / 1000), timestamp, previous_time; + unsigned int i, track; + int size, past = 0; + int64_t position; + VC_CONTAINER_PARAM_UNUSED(mode); + + /* If we have a video track, then we want to find the keyframe closest to + * the requested time, otherwise we just look for the tag with the + * closest timestamp */ + + /* Select the track on which we'll do our seeking */ + for(i = 0, track = 0; i < p_ctx->tracks_num; i++) + { + if(p_ctx->tracks[i]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) continue; + track = i; + break; + } + if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_CORRUPTED; + state = p_ctx->tracks[track]->priv->module->state; + previous_time = state->timestamp; + + LOG_DEBUG(p_ctx, "seek (%i, prev %i)", time, previous_time); + + if(state->index && vc_container_index_get(state->index, flags & VC_CONTAINER_SEEK_FLAG_FORWARD, + offset, &position, &past) == VC_CONTAINER_SUCCESS) + { + flv_update_track_position(p_ctx, track, position, 0, position, 0, (uint32_t) (*offset / 1000LL), 0); + } + else + { + if(time < state->timestamp / 2) + flv_update_track_position(p_ctx, track, (int64_t)module->data_offset, 0, + (int64_t)module->data_offset, 0, 0, 0); + past = 1; + } + + /* If past it clear then we're done, otherwise we need to find our point from here */ + if(past == 0) + { + status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + } + else + { + if(time > previous_time) + { + while(!status) + { + status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + if(status) break; + + /* Check if we have our frame */ + if(time <= timestamp) break; + + last_state = *state; + state->data_offset = size; /* consume data */ + } + } + else + { + while(!status) + { + status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + if(status) break; + + /* Check if we have our frame */ + if(time >= timestamp) break; + + /* Detect when we've reached the 1st keyframe to avoid an infinite loop */ + if(state->timestamp == last_state.timestamp) break; + + last_state = *state; + state->data_offset = size; /* consume data */ + } + } + } + + if(status != VC_CONTAINER_SUCCESS && (flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + { + LOG_DEBUG(p_ctx, "seek failed (%i)", status); + return status; + } + else if(status != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "seek failed (%i), look for previous frame", status); + if(last_state.tag_position) *state = last_state; + else status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + } + + LOG_DEBUG(p_ctx, "seek done (%i)", timestamp); + state->status = VC_CONTAINER_SUCCESS; + last_state.status = VC_CONTAINER_SUCCESS; + + if(past == 1) + { + /* Make adjustment based on seek mode */ + if((flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && timestamp < time && timestamp < previous_time) + { + if(last_state.tag_position) *state = last_state; + else status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + } + else if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && timestamp > time) + { + if(last_state.tag_position) *state = last_state; + else status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0); + } + + LOG_DEBUG(p_ctx, "seek adjustment (%i)", timestamp); + } + + if(state->data_position == last_state.data_position) + status = SEEK(p_ctx, state->data_position); + + *offset = timestamp * INT64_C(1000); + + return VC_CONTAINER_SUCCESS; +} + +/****************************************************************************** +Global function definitions. +******************************************************************************/ + +VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = 0; + uint8_t buffer[4], type_flags; + unsigned int i, frames, audio_present, video_present; + uint32_t data_offset; + + /* Check the FLV marker */ + if( PEEK_BYTES(p_ctx, buffer, 4) < 4 ) goto error; + if( buffer[0] != 'F' || buffer[1] != 'L' || buffer[2] != 'V' ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + /* Check FLV version */ + if( buffer[3] > 4 ) + { + LOG_DEBUG(p_ctx, "Version too high: %d", buffer[3]); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + SKIP_BYTES(p_ctx, 4); /* FLV marker and version */ + + /* Find out which tracks should be available. + * FLV can only have up to 1 audio track and 1 video track. */ + type_flags = READ_U8(p_ctx, "TypeFlags"); + audio_present = !!(type_flags & 0x04); + video_present = !!(type_flags & 0x01); + + /* Sanity check DataOffset */ + data_offset = READ_U32(p_ctx, "DataOffset"); + if(data_offset < 9) goto error; + + /* + * We are dealing with an FLV file + */ + + LOG_DEBUG(p_ctx, "using flv reader"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + module->data_offset = data_offset; + module->audio_track = -1; + module->video_track = -1; + + /* Skip to the start of the actual data */ + SKIP_BYTES(p_ctx, data_offset - 9); + + /* We'll start parsing a few of the FLV tags to find out the + * metadata / audio / video properties. + * The first tag we should see is the metadata one which will give us all the + * properties of the stream. However we do not rely on that being there and we + * actually look at the first audio / video tags as well. */ + for(frames = 0; frames < 20; frames++) + { + VC_CONTAINER_TRACK_T *track; + int64_t offset, skip; + int prev_size, type, size, channels, samplerate, bps; + uint32_t codec, timestamp; + + /* Stop there if we have everything we want */ + if(audio_present == (module->audio_track >= 0) && + video_present == (module->video_track >= 0)) break; + if(module->audio_track >= 0 && module->video_track >= 0) break; + + /* Start reading the next tag */ + if(flv_read_tag_header(p_ctx, &prev_size, &type, &size, ×tamp)) break; + if(!size) continue; + + offset = STREAM_POSITION(p_ctx); /* to keep track of how much data we read */ + + switch(type) + { + case FLV_TAG_TYPE_AUDIO: + if(module->audio_track >= 0) break; /* We already have our audio track */ + flv_read_audiodata_header(p_ctx, &codec, &samplerate, &channels, &bps); + + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + track->format->codec = codec; + flv_read_audiodata_properties(p_ctx, track, size - 1, samplerate, channels, bps); + + module->audio_track = p_ctx->tracks_num++; + track->is_enabled = 1; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + break; + + case FLV_TAG_TYPE_VIDEO: + if(module->video_track >= 0) break; /* We already have our video track */ + flv_read_videodata_header(p_ctx, &codec, 0); + + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + track->format->codec = codec; + + status = flv_read_videodata_properties(p_ctx, track, size - 1); + if(status != VC_CONTAINER_SUCCESS) { vc_container_free_track(p_ctx, track); break; } + + module->video_track = p_ctx->tracks_num++; + track->is_enabled = 1; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + break; + + case FLV_TAG_TYPE_METADATA: + flv_read_metadata(p_ctx, size); + break; + + default: break; + } + + /* Skip any data that's left unparsed from the current tag */ + skip = size - (STREAM_POSITION(p_ctx) - offset); + if(skip < 0) break; + SKIP_BYTES(p_ctx, (size_t)skip); + } + + /* Make sure we found something we can play */ + if(!p_ctx->tracks_num) {LOG_DEBUG(p_ctx, "didn't find any track"); goto error;} + + /* Try and create an index. All times are signed, so adding a base timestamp + * of zero means that we will always seek back to the start of the file, even if + * the actual frame timestamps start at some higher number. */ + if(vc_container_index_create(&module->state.index, 512) == VC_CONTAINER_SUCCESS) + vc_container_index_add(module->state.index, 0LL, (int64_t) data_offset); + + /* Use the metadata we read */ + if(module->audio_track >= 0) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->audio_track]; + track->format->bitrate = module->meta_audiodatarate; + } + if(module->video_track >= 0) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->video_track]; + track->format->bitrate = module->meta_videodatarate; + if(module->meta_framerate) + { + track->format->type->video.frame_rate_num = (uint32_t)(100 * module->meta_framerate); + track->format->type->video.frame_rate_den = 100; + } + + if(module->meta_width && module->meta_width > track->format->type->video.width) + track->format->type->video.width = module->meta_width; + if(module->meta_height && module->meta_height > track->format->type->video.height) + track->format->type->video.height = module->meta_height; + } + + status = SEEK(p_ctx, data_offset); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* Some initialisation */ + module->state.tag_position = data_offset; + module->state.data_position = data_offset; + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[i]->priv->module; + track_module->state = &module->state; + } + + if(STREAM_SEEKABLE(p_ctx)) + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + p_ctx->priv->pf_close = flv_reader_close; + p_ctx->priv->pf_read = flv_reader_read; + p_ctx->priv->pf_seek = flv_reader_seek; + + return VC_CONTAINER_SUCCESS; + + error: + if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + LOG_DEBUG(p_ctx, "flv: error opening stream"); + if(module) flv_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open flv_reader_open +#endif diff --git a/containers/h264/avc1_packetizer.c b/containers/h264/avc1_packetizer.c new file mode 100755 index 0000000..8082f0b --- /dev/null +++ b/containers/h264/avc1_packetizer.c @@ -0,0 +1,343 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Implementation of an ISO 14496-15 to Annexe-B AVC video packetizer. + */ + +#include +#include + +#include "containers/packetizers.h" +#include "containers/core/packetizers_private.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_bytestream.h" + +#ifndef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#endif + +/** Arbitrary number which should be sufficiently high so that no sane frame will + * be bigger than that. */ +#define MAX_FRAME_SIZE (1920*1088*2) + +VC_CONTAINER_STATUS_T avc1_packetizer_open( VC_PACKETIZER_T * ); + +/*****************************************************************************/ +typedef struct VC_PACKETIZER_MODULE_T { + enum { + STATE_FRAME_WAIT = 0, + STATE_BUFFER_INIT, + STATE_NAL_START, + STATE_NAL_DATA, + } state; + + unsigned int length_size; + + unsigned int frame_size; + unsigned int bytes_read; + unsigned int start_code_bytes_left; + unsigned int nal_bytes_left; + +} VC_PACKETIZER_MODULE_T; + +static const uint8_t h264_start_code[] = {0, 0, 0, 1}; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avc1_packetizer_close( VC_PACKETIZER_T *p_ctx ) +{ + free(p_ctx->priv->module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avc1_packetizer_reset( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + module->state = STATE_FRAME_WAIT; + module->frame_size = 0; + module->bytes_read = 0; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avc1_packetizer_packetize( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + VC_CONTAINER_PACKET_T *packet; + unsigned int offset, size, nal_num; + uint8_t data[4]; + VC_CONTAINER_PARAM_UNUSED(nal_num); + + while(1) switch (module->state) + { + case STATE_FRAME_WAIT: + for (packet = stream->current, size = 0; + packet && !(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END); + packet = packet->next) + size += packet->size; + if (!packet) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */ + + size += packet->size; + + /* We now have a complete frame available */ + + module->nal_bytes_left = 0; + module->start_code_bytes_left = 0; + + /* Find out the number of NAL units and size of the frame */ + for (offset = nal_num = 0; offset + module->length_size < size; nal_num++) + { + unsigned int nal_size; + + bytestream_peek_at(stream, offset, data, module->length_size); + offset += module->length_size; + + nal_size = data[0]; + if (module->length_size > 1) + nal_size = (nal_size << 8)|data[1]; + if (module->length_size > 2) + nal_size = (nal_size << 8)|data[2]; + if (module->length_size > 3) + nal_size = (nal_size << 8)|data[3]; + if (offset + nal_size > size) + nal_size = size - offset; + + offset += nal_size; + module->frame_size += nal_size + sizeof(h264_start_code); +#ifdef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE + LOG_DEBUG(0, "nal unit size %u", nal_size); +#endif + } + LOG_DEBUG(0, "frame size: %u(%u/%u), pts: %"PRIi64, module->frame_size, + size, nal_num, stream->current->pts); + + /* fall through to the next state */ + module->state = STATE_BUFFER_INIT; + + case STATE_BUFFER_INIT: + packet = stream->current; + out->size = module->frame_size - module->bytes_read; + out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; + out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + + if (!module->bytes_read) + { + out->pts = packet->pts; + out->dts = packet->dts; + out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + + if (flags & VC_PACKETIZER_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + if (flags & VC_PACKETIZER_FLAG_SKIP) + { + /* The easiest is to just drop all the packets belonging to the frame */ + while (!(stream->current->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END)) + bytestream_skip_packet(stream); + bytestream_skip_packet(stream); + + module->frame_size = 0; + module->bytes_read = 0; + return VC_CONTAINER_SUCCESS; + } + + /* We now know that we'll have to read some data so reset the output size */ + out->size = 0; + + /* Go to the next relevant state */ + module->state = STATE_NAL_START; + if (module->nal_bytes_left || module->bytes_read == module->frame_size) + module->state = STATE_NAL_DATA; + break; + + case STATE_NAL_START: + /* Extract the size of the current NAL */ + bytestream_get(stream, data, module->length_size); + + module->nal_bytes_left = data[0]; + if (module->length_size > 1) + module->nal_bytes_left = (module->nal_bytes_left << 8)|data[1]; + if (module->length_size > 2) + module->nal_bytes_left = (module->nal_bytes_left << 8)|data[2]; + if (module->length_size > 3) + module->nal_bytes_left = (module->nal_bytes_left << 8)|data[3]; + + if (module->bytes_read + module->nal_bytes_left + sizeof(h264_start_code) > + module->frame_size) + { + LOG_ERROR(0, "truncating nal (%u/%u)", module->nal_bytes_left, + module->frame_size - module->bytes_read - sizeof(h264_start_code)); + module->nal_bytes_left = module->frame_size - sizeof(h264_start_code); + } + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE + LOG_DEBUG(0, "nal unit size %u", module->nal_bytes_left); +#endif + + module->start_code_bytes_left = sizeof(h264_start_code); + + /* fall through to the next state */ + module->state = STATE_NAL_DATA; + + case STATE_NAL_DATA: + /* Start by adding the start code */ + if (module->start_code_bytes_left) + { + size = MIN(out->buffer_size - out->size, module->start_code_bytes_left); + memcpy(out->data + out->size, h264_start_code + sizeof(h264_start_code) - + module->start_code_bytes_left, size); + module->start_code_bytes_left -= size; + module->bytes_read += size; + out->size += size; + } + + /* Then append the NAL unit itself */ + if (module->nal_bytes_left) + { + size = MIN(out->buffer_size - out->size, module->nal_bytes_left); + bytestream_get( stream, out->data + out->size, size ); + module->nal_bytes_left -= size; + module->bytes_read += size; + out->size += size; + } + + /* Check whether we're done */ + if (module->bytes_read == module->frame_size) + { + bytestream_skip_packet(stream); + module->state = STATE_FRAME_WAIT; + module->frame_size = 0; + module->bytes_read = 0; + return VC_CONTAINER_SUCCESS; + } + else if (out->buffer_size == out->size) + { + out->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + module->state = STATE_BUFFER_INIT; + return VC_CONTAINER_SUCCESS; + } + + /* We're not done, go to the next relevant state */ + module->state = STATE_NAL_START; + break; + + default: + break; + }; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T avc1_packetizer_codecconfig( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint8_t *out, *extra = p_ctx->in->extradata + 5; + uint8_t *extra_end = extra + p_ctx->in->extradata_size - 5; + unsigned int i, j, nal_size, out_size = 0; + + if (p_ctx->in->extradata_size <= 5 || + p_ctx->in->extradata[0] != 1 /* configurationVersion */) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + status = vc_container_format_extradata_alloc(p_ctx->out, p_ctx->in->extradata_size); + if (status != VC_CONTAINER_SUCCESS) + return status; + + out = p_ctx->out->extradata; + module->length_size = (*(p_ctx->in->extradata + 4) & 0x3) + 1; + + for (i = 0; i < 2 && extra < extra_end - 1; i++) + { + j = *(extra++) & (!i ? 0x1F : 0xFF); + for (; j > 0 && extra < extra_end - 2; j--) + { + nal_size = (extra[0] << 8) | extra[1]; extra += 2; + if (extra + nal_size > extra_end) + { + extra = extra_end; + break; + } + + out[0] = out[1] = out[2] = 0; out[3] = 1; + memcpy(out + 4, extra, nal_size); + out += nal_size + 4; extra += nal_size; + out_size += nal_size + 4; + } + } + + p_ctx->out->extradata_size = out_size; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T avc1_packetizer_open( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module; + VC_CONTAINER_STATUS_T status; + + if(p_ctx->in->codec != VC_CONTAINER_CODEC_H264 && + p_ctx->out->codec != VC_CONTAINER_CODEC_H264) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(p_ctx->in->codec_variant != VC_CONTAINER_VARIANT_H264_AVC1 && + p_ctx->out->codec_variant != VC_CONTAINER_VARIANT_H264_DEFAULT) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(!(p_ctx->in->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + p_ctx->priv->module = module = malloc(sizeof(*module)); + if(!module) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + + vc_container_format_copy(p_ctx->out, p_ctx->in, 0); + status = avc1_packetizer_codecconfig(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + { + free(module); + return status; + } + + p_ctx->out->codec_variant = VC_CONTAINER_VARIANT_H264_DEFAULT; + p_ctx->max_frame_size = MAX_FRAME_SIZE; + p_ctx->priv->pf_close = avc1_packetizer_close; + p_ctx->priv->pf_packetize = avc1_packetizer_packetize; + p_ctx->priv->pf_reset = avc1_packetizer_reset; + LOG_DEBUG(0, "using avc1 video packetizer"); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_PACKETIZER_REGISTER(avc1_packetizer_open, "avc1"); diff --git a/containers/io/io_file.c b/containers/io/io_file.c new file mode 100755 index 0000000..ea4b945 --- /dev/null +++ b/containers/io/io_file.c @@ -0,0 +1,154 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_uri.h" + +typedef struct VC_CONTAINER_IO_MODULE_T +{ + FILE *stream; + +} VC_CONTAINER_IO_MODULE_T; + +VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *, const char *, + VC_CONTAINER_IO_MODE_T ); + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_file_close( VC_CONTAINER_IO_T *p_ctx ) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + fclose(module->stream); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_file_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + size_t ret = fread(buffer, 1, size, p_ctx->module->stream); + if(ret != size) + { + /* Sanity check return value. Some platforms (e.g. Android) can return -1 */ + if( ((int)ret) < 0 ) ret = 0; + + if( feof(p_ctx->module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; + else p_ctx->status = VC_CONTAINER_ERROR_FAILED; + } + return ret; +} + +/*****************************************************************************/ +static size_t io_file_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + return fwrite(buffer, 1, size, p_ctx->module->stream); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_file_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int ret; + + //FIXME: large file support +#ifdef _VIDEOCORE + extern int fseek64(FILE *fp, int64_t offset, int whence); + ret = fseek64(p_ctx->module->stream, offset, SEEK_SET); +#else + if (offset > (int64_t)UINT_MAX) + { + p_ctx->status = VC_CONTAINER_ERROR_EOS; + return VC_CONTAINER_ERROR_EOS; + } + ret = fseek(p_ctx->module->stream, (long)offset, SEEK_SET); +#endif + if(ret) + { + if( feof(p_ctx->module->stream) ) status = VC_CONTAINER_ERROR_EOS; + else status = VC_CONTAINER_ERROR_FAILED; + } + + p_ctx->status = status; + return status; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *p_ctx, + const char *unused, VC_CONTAINER_IO_MODE_T mode ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = 0; + const char *psz_mode = mode == VC_CONTAINER_IO_MODE_WRITE ? "wb+" : "rb"; + const char *uri = p_ctx->uri; + FILE *stream = 0; + VC_CONTAINER_PARAM_UNUSED(unused); + + if(vc_uri_path(p_ctx->uri_parts)) + uri = vc_uri_path(p_ctx->uri_parts); + + stream = fopen(uri, psz_mode); + if(!stream) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } + + /* Turn off buffering. The container layer will provide its own cache */ + setvbuf(stream, NULL, _IONBF, 0); + + module = malloc( sizeof(*module) ); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + + p_ctx->module = module; + module->stream = stream; + p_ctx->pf_close = io_file_close; + p_ctx->pf_read = io_file_read; + p_ctx->pf_write = io_file_write; + p_ctx->pf_seek = io_file_seek; + + if(mode == VC_CONTAINER_IO_MODE_WRITE) + { + p_ctx->max_size = (1UL<<31)-1; /* For now limit to 2GB */ + } + else + { + //FIXME: large file support, platform-specific file size + fseek(p_ctx->module->stream, 0, SEEK_END); + p_ctx->size = ftell(p_ctx->module->stream); + fseek(p_ctx->module->stream, 0, SEEK_SET); + } + + p_ctx->capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING; + return VC_CONTAINER_SUCCESS; + + error: + if(stream) fclose(stream); + return status; +} diff --git a/containers/io/io_http.c b/containers/io/io_http.c new file mode 100755 index 0000000..48d8661 --- /dev/null +++ b/containers/io/io_http.c @@ -0,0 +1,898 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_uri.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_list.h" +#include "containers/core/containers_utils.h" +#include "containers/net/net_sockets.h" + +/* Set to 1 if you want to log all HTTP requests */ +#define ENABLE_HTTP_EXTRA_LOGGING 0 + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +#define IO_HTTP_DEFAULT_PORT "80" + +/** Space for sending requests and receiving responses */ +#define COMMS_BUFFER_SIZE 4000 + +/** Largest allowed HTTP URI. Must be substantially smaller than COMMS_BUFFER_SIZE + * to allow for the headers that may be sent. */ +#define HTTP_URI_LENGTH_MAX 1024 + +/** Initial capacity of header list */ +#define HEADER_LIST_INITIAL_CAPACITY 16 + +/** Format of the first line of an HTTP request */ +#define HTTP_REQUEST_LINE_FORMAT "%s %s HTTP/1.1\r\nHost: %s\r\n" + +/** Format of a range request */ +#define HTTP_RANGE_REQUEST "Range: bytes=%"PRId64"-%"PRId64"\r\n" + +/** Format string for common headers used with all request methods. + * Note: includes double new line to terminate headers */ +#define TRAILING_HEADERS_FORMAT "User-Agent: Broadcom/1.0\r\n\r\n" + +/** \name HTTP methods, used as the first item in the request line + * @{ */ +#define GET_METHOD "GET" +#define HEAD_METHOD "HEAD" +/* @} */ + +/** \name Names of headers used by the code + * @{ */ +#define CONTENT_LENGTH_NAME "Content-Length" +#define CONTENT_BASE_NAME "Content-Base" +#define CONTENT_LOCATION_NAME "Content-Location" +#define ACCEPT_RANGES_NAME "Accept-Ranges" +#define CONNECTION_NAME "Connection" +/* @} */ + +/** Supported HTTP major version number */ +#define HTTP_MAJOR_VERSION 1 +/** Supported HTTP minor version number */ +#define HTTP_MINOR_VERSION 1 + +/** Lowest successful status code value */ +#define HTTP_STATUS_OK 200 +#define HTTP_STATUS_PARTIAL_CONTENT 206 + +typedef struct http_header_tag { + const char *name; + char *value; +} HTTP_HEADER_T; + + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_IO_MODULE_T +{ + VC_CONTAINER_NET_T *sock; + VC_CONTAINERS_LIST_T *header_list; /**< Parsed response headers, pointing into comms buffer */ + + bool persistent; + int64_t cur_offset; + bool reconnecting; + + /* Buffer used for sending and receiving HTTP messages */ + char comms_buffer[COMMS_BUFFER_SIZE]; +} VC_CONTAINER_IO_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ + +static int io_http_header_comparator(const HTTP_HEADER_T *first, const HTTP_HEADER_T *second); +static VC_CONTAINER_STATUS_T io_http_send(VC_CONTAINER_IO_T *p_ctx); + +VC_CONTAINER_STATUS_T vc_container_io_http_open(VC_CONTAINER_IO_T *, const char *, + VC_CONTAINER_IO_MODE_T); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/**************************************************************************//** + * Trim whitespace from the end and start of the string + * + * \param str String to be trimmed + * \return Trimmed string + */ +static char *io_http_trim(char *str) +{ + char *s = str + strlen(str); + + /* Search backwards for first non-whitespace */ + while (--s >= str &&(*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')) + ; /* Everything done in the while */ + s[1] = '\0'; + + /* Now move start of string forwards to first non-whitespace */ + s = str; + while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') + s++; + + return s; +} + +/**************************************************************************//** + * Header comparison function. + * Compare two header structures and return whether the first is less than, + * equal to or greater than the second. + * + * @param first The first structure to be compared. + * @param second The second structure to be compared. + * @return Negative if first is less than second, positive if first is greater + * and zero if they are equal. + */ +static int io_http_header_comparator(const HTTP_HEADER_T *first, const HTTP_HEADER_T *second) +{ + return strcasecmp(first->name, second->name); +} + +/**************************************************************************//** + * Check a response status line to see if the response is usable or not. + * Reasons for invalidity include: + * - Incorrectly formatted + * - Unsupported version + * - Status code is not in the 2xx range + * + * @param status_line The response status line. + * @return The resulting status of the function. + */ +static bool io_http_successful_response_status(const char *status_line) +{ + unsigned int major_version, minor_version, status_code; + + /* coverity[secure_coding] String is null-terminated */ + if (sscanf(status_line, "HTTP/%u.%u %u", &major_version, &minor_version, &status_code) != 3) + { + LOG_ERROR(NULL, "HTTP: Invalid response status line:\n%s", status_line); + return false; + } + + if (major_version != HTTP_MAJOR_VERSION || minor_version != HTTP_MINOR_VERSION) + { + LOG_ERROR(NULL, "HTTP: Unexpected response HTTP version: %u.%u", major_version, minor_version); + return false; + } + + if (status_code != HTTP_STATUS_OK && status_code != HTTP_STATUS_PARTIAL_CONTENT) + { + LOG_ERROR(NULL, "HTTP: Response status unsuccessful:\n%s", status_line); + return false; + } + + return true; +} + +/**************************************************************************//** + * Get the content length header from the response headers as an unsigned + * 64-bit integer. + * If the content length header is not found or badly formatted, zero is + * returned. + * + * @param header_list The response headers. + * @return The content length. + */ +static uint64_t io_http_get_content_length(VC_CONTAINERS_LIST_T *header_list) +{ + uint64_t content_length = 0; + HTTP_HEADER_T header; + + header.name = CONTENT_LENGTH_NAME; + if (header_list && vc_containers_list_find_entry(header_list, &header)) + /* coverity[secure_coding] String is null-terminated */ + sscanf(header.value, "%"PRIu64, &content_length); + + return content_length; +} + +/**************************************************************************//** + * Get the accept ranges header from the response headers and verify that + * the server accepts byte ranges.. + * If the accept ranges header is not found false is returned. + * + * @param header_list The response headers. + * @return The resulting status of the function. + */ +static bool io_http_check_accept_range(VC_CONTAINERS_LIST_T *header_list) +{ + HTTP_HEADER_T header; + + header.name = ACCEPT_RANGES_NAME; + if (header_list && vc_containers_list_find_entry(header_list, &header)) + { + /* coverity[secure_coding] String is null-terminated */ + if (!strcasecmp(header.value, "bytes")) + return true; + } + + return false; +} + +/**************************************************************************//** + * Check whether the server supports persistent connections. + * + * @param header_list The response headers. + * @return The resulting status of the function. + */ +static bool io_http_check_persistent_connection(VC_CONTAINERS_LIST_T *header_list) +{ + HTTP_HEADER_T header; + + header.name = CONNECTION_NAME; + if (header_list && vc_containers_list_find_entry(header_list, &header)) + { + /* coverity[secure_coding] String is null-terminated */ + if (!strcasecmp(header.value, "close")) + return false; + } + + return true; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T translate_net_status_to_container_status(vc_container_net_status_t net_status) +{ + switch (net_status) + { + case VC_CONTAINER_NET_SUCCESS: return VC_CONTAINER_SUCCESS; + case VC_CONTAINER_NET_ERROR_INVALID_SOCKET: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_NOT_ALLOWED: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + case VC_CONTAINER_NET_ERROR_INVALID_PARAMETER: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_NO_MEMORY: return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + case VC_CONTAINER_NET_ERROR_IN_USE: return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + case VC_CONTAINER_NET_ERROR_NETWORK: return VC_CONTAINER_ERROR_EOS; + case VC_CONTAINER_NET_ERROR_CONNECTION_LOST: return VC_CONTAINER_ERROR_EOS; + case VC_CONTAINER_NET_ERROR_NOT_CONNECTED: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_TIMED_OUT: return VC_CONTAINER_ERROR_ABORTED; + case VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED: return VC_CONTAINER_ERROR_NOT_FOUND; + case VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND: return VC_CONTAINER_ERROR_NOT_FOUND; + case VC_CONTAINER_NET_ERROR_TRY_AGAIN: return VC_CONTAINER_ERROR_CONTINUE; + default: return VC_CONTAINER_ERROR_FAILED; + } +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_http_open_socket(VC_CONTAINER_IO_T *ctx) +{ + VC_CONTAINER_IO_MODULE_T *module = ctx->module; + VC_CONTAINER_STATUS_T status; + const char *host, *port; + + /* Treat empty host or port strings as not defined */ + port = vc_uri_port(ctx->uri_parts); + if (port && !*port) + port = NULL; + + /* Require the port to be defined */ + if (!port) + { + status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; + goto error; + } + + host = vc_uri_host(ctx->uri_parts); + if (host && !*host) + host = NULL; + + if (!host) + { + status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; + goto error; + } + + module->sock = vc_container_net_open(host, port, VC_CONTAINER_NET_OPEN_FLAG_STREAM, NULL); + if (!module->sock) + { + status = VC_CONTAINER_ERROR_URI_NOT_FOUND; + goto error; + } + + return VC_CONTAINER_SUCCESS; + +error: + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_http_close_socket(VC_CONTAINER_IO_MODULE_T *module) +{ + if (module->sock) + { + vc_container_net_close(module->sock); + module->sock = NULL; + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_http_read_from_net(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + size_t ret; + vc_container_net_status_t net_status; + + ret = vc_container_net_read(p_ctx->module->sock, buffer, size); + net_status = vc_container_net_status(p_ctx->module->sock); + p_ctx->status = translate_net_status_to_container_status(net_status); + + return ret; +} + +/**************************************************************************//** + * Reads an HTTP response and parses it into headers and content. + * The headers and content remain stored in the comms buffer, but referenced + * by the module's header list. Content uses a special header name that cannot + * occur in the real headers. + * + * @param p_ctx The HTTP reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T io_http_read_response(VC_CONTAINER_IO_T *p_ctx) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + char *next_read = module->comms_buffer; + size_t space_available = sizeof(module->comms_buffer) - 1; /* Allow for a NUL */ + char *ptr = next_read; + bool end_response = false; + HTTP_HEADER_T header; + const char endstr[] = "\r\n\r\n"; + int endcount = sizeof(endstr) - 1; + int endchk = 0; + + vc_containers_list_reset(module->header_list); + + /* Response status line doesn't need to be stored, just checked */ + header.name = NULL; + header.value = next_read; + + /* + * We need to read just a byte at a time to make sure that we just read the HTTP response and + * no more. For example, if a GET operation was requested the file being fetched will also + * be waiting to be read on the socket. + */ + + while (space_available) + { + if (io_http_read_from_net(p_ctx, next_read, 1) != 1) + break; + + next_read++; + space_available--; + + if (next_read[-1] == endstr[endchk]) + { + if (++endchk == endcount) + break; + } + else + endchk = 0; + } + if (!space_available) + { + LOG_ERROR(NULL, "comms buffer too small for complete HTTP message (%d)", + sizeof(module->comms_buffer)); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + *next_read = '\0'; + + if (endchk == endcount) + { + if (ENABLE_HTTP_EXTRA_LOGGING) + LOG_DEBUG(NULL, "READ FROM SERVER: %d bytes\n%s\n-----------------------------------------", + sizeof(module->comms_buffer) - 1 - space_available, module->comms_buffer); + + while (!end_response && ptr < next_read) + { + switch (*ptr) + { + case ':': + if (header.value) + { + /* Just another character in the value */ + ptr++; + } else { + /* End of name, expect value next */ + *ptr++ = '\0'; + header.value = ptr; + } + break; + + case '\n': + if (header.value) + { + /* End of line while parsing the value part of the header, add name/value pair to list */ + *ptr++ = '\0'; + header.value = io_http_trim(header.value); + if (header.name) + { + if (!vc_containers_list_insert(module->header_list, &header, false)) + { + LOG_ERROR(NULL, "HTTP: Failed to add <%s> header to list", header.name); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + } else { + /* Check response status line */ + if (!io_http_successful_response_status(header.value)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + /* Ready for next header */ + header.name = ptr; + header.value = NULL; + } else { + /* End of line while parsing the name of a header */ + *ptr++ = '\0'; + if (*header.name && *header.name != '\r') + { + /* A non-empty name is invalid, so fail */ + LOG_ERROR(NULL, "HTTP: Invalid name in header - no colon:\n%s", header.name); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* An empty name signifies the end of the HTTP response */ + end_response = true; + } + break; + + default: + /* Just another character in either the name or the value */ + ptr++; + } + } + } + + if (!space_available && !end_response) + { + /* Ran out of buffer space */ + LOG_ERROR(NULL, "HTTP: Response header section too big"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + return p_ctx->status; +} + +/**************************************************************************//** + * Send a GET request to the HTTP server. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T io_http_send_get_request(VC_CONTAINER_IO_T *p_ctx, size_t size) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + char *ptr = module->comms_buffer, *end = ptr + sizeof(module->comms_buffer); + int64_t end_offset; + + ptr += snprintf(ptr, end - ptr, HTTP_REQUEST_LINE_FORMAT, GET_METHOD, + vc_uri_path(p_ctx->uri_parts), vc_uri_host(p_ctx->uri_parts)); + + end_offset = module->cur_offset + size - 1; + if (end_offset >= p_ctx->size) + end_offset = p_ctx->size - 1; + + if (ptr < end) + ptr += snprintf(ptr, end - ptr, HTTP_RANGE_REQUEST, module->cur_offset, end_offset); + + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT); + + if (ptr >= end) + { + LOG_ERROR(0, "comms buffer too small (%i/%u)", (int)(end - ptr), + sizeof(module->comms_buffer)); + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + } + + if (ENABLE_HTTP_EXTRA_LOGGING) + LOG_DEBUG(NULL, "Sending server read request:\n%s\n---------------------\n", module->comms_buffer); + return io_http_send(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_http_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + + /* + * No seeking past the end of the file. + */ + + if (offset < 0 || offset > p_ctx->size) + { + p_ctx->status = VC_CONTAINER_ERROR_EOS; + return VC_CONTAINER_ERROR_EOS; + } + + module->cur_offset = offset; + p_ctx->status = VC_CONTAINER_SUCCESS; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_http_close(VC_CONTAINER_IO_T *p_ctx) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + + if (!module) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + io_http_close_socket(module); + if (module->header_list) + vc_containers_list_destroy(module->header_list); + + free(module); + p_ctx->module = NULL; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_http_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + size_t content_length; + size_t bytes_read; + size_t ret = 0; + char *ptr = buffer; + + /* + * Are we at the end of the file? + */ + + if (module->cur_offset >= p_ctx->size) + { + p_ctx->status = VC_CONTAINER_ERROR_EOS; + return 0; + } + + if (!module->persistent) + { + status = io_http_open_socket(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(NULL, "Error opening socket for GET request"); + return status; + } + } + + /* Send GET request and get response */ + status = io_http_send_get_request(p_ctx, size); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(NULL, "Error sending GET request"); + goto error; + } + + status = io_http_read_response(p_ctx); + if (status == VC_CONTAINER_ERROR_EOS && !module->reconnecting) + { + LOG_DEBUG(NULL, "reconnecting"); + io_http_close_socket(module); + status = io_http_open_socket(p_ctx); + if (status == VC_CONTAINER_SUCCESS) + { + module->reconnecting = true; + status = io_http_read(p_ctx, buffer, size); + module->reconnecting = false; + return status; + } + } + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(NULL, "Error reading GET response"); + goto error; + } + + /* + * How much data is the server offering us? + */ + + content_length = (size_t)io_http_get_content_length(module->header_list); + if (content_length > size) + { + LOG_ERROR(NULL, "received too much data (%i/%i)", + (int)content_length, (int)size); + status = VC_CONTAINER_ERROR_CORRUPTED; + goto error; + } + + bytes_read = 0; + while (bytes_read < content_length && p_ctx->status == VC_CONTAINER_SUCCESS) + { + ret = io_http_read_from_net(p_ctx, ptr, content_length - bytes_read); + if (p_ctx->status == VC_CONTAINER_SUCCESS) + { + bytes_read += ret; + ptr += ret; + } + } + + if (p_ctx->status == VC_CONTAINER_SUCCESS) + { + module->cur_offset += bytes_read; + ret = bytes_read; + } + + if (!module->persistent) + io_http_close_socket(module); + + return ret; + +error: + if (!module->persistent) + io_http_close_socket(module); + + return status; +} + +/*****************************************************************************/ +static size_t io_http_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + size_t ret = vc_container_net_write(p_ctx->module->sock, buffer, size); + vc_container_net_status_t net_status; + + net_status = vc_container_net_status(p_ctx->module->sock); + p_ctx->status = translate_net_status_to_container_status(net_status); + + return ret; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_http_control(struct VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_CONTROL_T operation, + va_list args) +{ + vc_container_net_status_t net_status; + VC_CONTAINER_STATUS_T status; + + switch (operation) + { + case VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE: + net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE, args); + break; + case VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS: + net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, args); + break; + default: + net_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + } + + status = translate_net_status_to_container_status(net_status); + p_ctx->status = status; + + return status; +} + +/**************************************************************************//** + * Send out the data in the comms buffer. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T io_http_send(VC_CONTAINER_IO_T *p_ctx) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + size_t to_write; + size_t written; + const char *buffer = module->comms_buffer; + + to_write = strlen(buffer); + + while (to_write) + { + written = io_http_write(p_ctx, buffer, to_write); + if (p_ctx->status != VC_CONTAINER_SUCCESS) + break; + + to_write -= written; + buffer += written; + } + + return p_ctx->status; +} + +/**************************************************************************//** + * Send a HEAD request to the HTTP server. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T io_http_send_head_request(VC_CONTAINER_IO_T *p_ctx) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + char *ptr = module->comms_buffer, *end = ptr + sizeof(module->comms_buffer); + + ptr += snprintf(ptr, end - ptr, HTTP_REQUEST_LINE_FORMAT, HEAD_METHOD, + vc_uri_path(p_ctx->uri_parts), vc_uri_host(p_ctx->uri_parts)); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT); + + if (ptr >= end) + { + LOG_ERROR(0, "comms buffer too small (%i/%u)", (int)(end - ptr), + sizeof(module->comms_buffer)); + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + } + + return io_http_send(p_ctx); +} + +static VC_CONTAINER_STATUS_T io_http_head(VC_CONTAINER_IO_T *p_ctx) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + uint64_t content_length; + + /* Send HEAD request and get response */ + status = io_http_send_head_request(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + return status; + status = io_http_read_response(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + return status; + + /* + * Save the content length since that's our file size. + */ + + content_length = io_http_get_content_length(module->header_list); + if (content_length) + { + p_ctx->size = content_length; + LOG_DEBUG(NULL, "File size is %"PRId64, p_ctx->size); + } + + /* + * Now make sure that the server supports byte range requests. + */ + + if (!io_http_check_accept_range(module->header_list)) + { + LOG_ERROR(NULL, "Server doesn't support byte range requests"); + return VC_CONTAINER_ERROR_FAILED; + } + + /* + * Does it support persistent connections? + */ + + if (io_http_check_persistent_connection(module->header_list)) + { + module->persistent = true; + } + else + { + LOG_DEBUG(NULL, "Server does not support persistent connections"); + io_http_close_socket(module); + } + + module->cur_offset = 0; + + return status; +} + +/***************************************************************************** +Functions exported as part of the I/O Module API + *****************************************************************************/ + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_http_open(VC_CONTAINER_IO_T *p_ctx, + const char *unused, VC_CONTAINER_IO_MODE_T mode) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = 0; + VC_CONTAINER_PARAM_UNUSED(unused); + + /* Check the URI to see if we're dealing with an http stream */ + if (!vc_uri_scheme(p_ctx->uri_parts) || + strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "http")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* + * Some basic error checking. + */ + + if (mode == VC_CONTAINER_IO_MODE_WRITE) + { + status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + goto error; + } + + if (strlen(p_ctx->uri) > HTTP_URI_LENGTH_MAX) + { + status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; + goto error; + } + + module = calloc(1, sizeof(*module)); + if (!module) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + p_ctx->module = module; + + /* header_list will contain pointers into the response_buffer, so take care in re-use */ + module->header_list = vc_containers_list_create(HEADER_LIST_INITIAL_CAPACITY, sizeof(HTTP_HEADER_T), + (VC_CONTAINERS_LIST_COMPARATOR_T)io_http_header_comparator); + if (!module->header_list) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + + /* + * Make sure that we have a port number. + */ + + if (vc_uri_port(p_ctx->uri_parts) == NULL) + vc_uri_set_port(p_ctx->uri_parts, IO_HTTP_DEFAULT_PORT); + + status = io_http_open_socket(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + /* + * Whoo hoo! Our socket is open. Now let's send a HEAD request. + */ + + status = io_http_head(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + p_ctx->pf_close = io_http_close; + p_ctx->pf_read = io_http_read; + p_ctx->pf_write = NULL; + p_ctx->pf_control = io_http_control; + p_ctx->pf_seek = io_http_seek; + + p_ctx->capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING; + p_ctx->capabilities |= VC_CONTAINER_IO_CAPS_SEEK_SLOW; + + return VC_CONTAINER_SUCCESS; + +error: + io_http_close(p_ctx); + return status; +} diff --git a/containers/io/io_net.c b/containers/io/io_net.c new file mode 100755 index 0000000..a1912e5 --- /dev/null +++ b/containers/io/io_net.c @@ -0,0 +1,379 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_uri.h" +#include "containers/net/net_sockets.h" + +/* Uncomment this macro definition to capture data read and written through this interface */ +/* #define IO_NET_CAPTURE_PACKETS */ + +#ifdef IO_NET_CAPTURE_PACKETS +#include + +#ifdef ENABLE_CONTAINERS_STANDALONE +#ifdef _MSC_VER +#define IO_NET_CAPTURE_PREFIX "C:\\" +#else /* !_MSC_VER */ +#define IO_NET_CAPTURE_PREFIX "~/" +#endif +#else /* !ENABLE_CONTAINERS_STANDALONE */ +#define IO_NET_CAPTURE_PREFIX "/mfs/sd/" +#endif + +#define IO_NET_CAPTURE_READ_FILE "capture_read_%s_%s%c.pkt" +#define IO_NET_CAPTURE_WRITE_FILE "capture_write_%s_%s%c.pkt" +#define IO_NET_CAPTURE_READ_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_READ_FILE +#define IO_NET_CAPTURE_WRITE_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_WRITE_FILE + +#define CAPTURE_FILENAME_BUFFER_SIZE 300 + +#define CAPTURE_BUFFER_SIZE 65536 + +/** Native byte order word */ +#define NATIVE_BYTE_ORDER 0x50415753 +#endif + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_IO_MODULE_T +{ + VC_CONTAINER_NET_T *sock; +#ifdef IO_NET_CAPTURE_PACKETS + FILE *read_capture_file; + FILE *write_capture_file; +#endif +} VC_CONTAINER_IO_MODULE_T; + +/** List of recognised network URI schemes (TCP or UDP). + * Note: always use lower case for the scheme name. */ +static struct +{ + const char *scheme; + bool is_udp; +} recognised_schemes[] = { + { "rtp:", true }, + { "rtsp:", false }, +}; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *, const char *, + VC_CONTAINER_IO_MODE_T ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +#ifdef IO_NET_CAPTURE_PACKETS +/*****************************************************************************/ +static FILE *io_net_open_capture_file(const char *host_str, + const char *port_str, + bool is_udp, + VC_CONTAINER_IO_MODE_T mode) +{ + char filename[CAPTURE_FILENAME_BUFFER_SIZE]; + const char *format; + FILE *stream = NULL; + uint32_t byte_order = NATIVE_BYTE_ORDER; + + switch (mode) + { + case VC_CONTAINER_IO_MODE_WRITE: + format = IO_NET_CAPTURE_WRITE_FORMAT; + break; + case VC_CONTAINER_IO_MODE_READ: + format = IO_NET_CAPTURE_READ_FORMAT; + break; + default: + /* Invalid mode */ + return NULL; + } + + if (!host_str) + host_str = ""; + if (!port_str) + port_str = ""; + + /* Check filename will fit in buffer */ + if (strlen(format) + strlen(host_str) + strlen(port_str) - 4 > CAPTURE_FILENAME_BUFFER_SIZE) + return NULL; + + /* Create the file */ + sprintf(filename, format, host_str, port_str, is_udp ? 'u' : 't'); + stream = fopen(filename, "wb"); + if (!stream) + return NULL; + + /* Buffer plenty of data at a time, if possible */ + setvbuf(stream, NULL, _IOFBF, CAPTURE_BUFFER_SIZE); + + /* Start file with a byte order marker */ + if (fwrite(&byte_order, 1, sizeof(byte_order), stream) != sizeof(byte_order)) + { + /* Failed to write even just the byte order mark - abort */ + fclose(stream); + stream = NULL; + remove(filename); + } + + return stream; +} + +/*****************************************************************************/ +static void io_net_capture_write_packet( FILE *stream, + const char *buffer, + uint32_t buffer_size ) +{ + if (stream && buffer && buffer_size) + { + fwrite(&buffer_size, 1, sizeof(buffer_size), stream); + fwrite(buffer, 1, buffer_size, stream); + } +} +#endif + +/*****************************************************************************/ +static bool io_net_recognise_scheme(const char *uri, bool *is_udp) +{ + size_t ii; + const char *scheme; + + if (!uri) + return false; + + for (ii = 0; ii < countof(recognised_schemes); ii++) + { + scheme = recognised_schemes[ii].scheme; + if (strncmp(scheme, uri, strlen(scheme)) == 0) + { + *is_udp = recognised_schemes[ii].is_udp; + return true; + } + } + + return false; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T translate_net_status_to_container_status(vc_container_net_status_t net_status) +{ + switch (net_status) + { + case VC_CONTAINER_NET_SUCCESS: return VC_CONTAINER_SUCCESS; + case VC_CONTAINER_NET_ERROR_INVALID_SOCKET: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_NOT_ALLOWED: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + case VC_CONTAINER_NET_ERROR_INVALID_PARAMETER: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_NO_MEMORY: return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + case VC_CONTAINER_NET_ERROR_IN_USE: return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + case VC_CONTAINER_NET_ERROR_NETWORK: return VC_CONTAINER_ERROR_EOS; + case VC_CONTAINER_NET_ERROR_CONNECTION_LOST: return VC_CONTAINER_ERROR_EOS; + case VC_CONTAINER_NET_ERROR_NOT_CONNECTED: return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + case VC_CONTAINER_NET_ERROR_TIMED_OUT: return VC_CONTAINER_ERROR_ABORTED; + case VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED: return VC_CONTAINER_ERROR_NOT_FOUND; + case VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND: return VC_CONTAINER_ERROR_NOT_FOUND; + case VC_CONTAINER_NET_ERROR_TRY_AGAIN: return VC_CONTAINER_ERROR_CONTINUE; + default: return VC_CONTAINER_ERROR_FAILED; + } +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_net_close( VC_CONTAINER_IO_T *p_ctx ) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + + if (!module) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + if (module->sock) + vc_container_net_close(module->sock); +#ifdef IO_NET_CAPTURE_PACKETS + if (module->read_capture_file) + fclose(module->read_capture_file); + if (module->write_capture_file) + fclose(module->write_capture_file); +#endif + free(module); + p_ctx->module = NULL; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_net_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + size_t ret = vc_container_net_read(p_ctx->module->sock, buffer, size); + vc_container_net_status_t net_status; + + net_status = vc_container_net_status(p_ctx->module->sock); + p_ctx->status = translate_net_status_to_container_status(net_status); + +#ifdef IO_NET_CAPTURE_PACKETS + if (p_ctx->status == VC_CONTAINER_SUCCESS) + io_net_capture_write_packet(p_ctx->module->read_capture_file, (const char *)buffer, ret); +#endif + + return ret; +} + +/*****************************************************************************/ +static size_t io_net_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + size_t ret = vc_container_net_write(p_ctx->module->sock, buffer, size); + vc_container_net_status_t net_status; + + net_status = vc_container_net_status(p_ctx->module->sock); + p_ctx->status = translate_net_status_to_container_status(net_status); + +#ifdef IO_NET_CAPTURE_PACKETS + if (p_ctx->status == VC_CONTAINER_SUCCESS) + io_net_capture_write_packet(p_ctx->module->write_capture_file, (const char *)buffer, ret); +#endif + + return ret; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_net_control(struct VC_CONTAINER_IO_T *p_ctx, + VC_CONTAINER_CONTROL_T operation, + va_list args) +{ + vc_container_net_status_t net_status; + VC_CONTAINER_STATUS_T status; + + switch (operation) + { + case VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE: + net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE, args); + break; + case VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS: + net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, args); + break; + default: + net_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + } + + status = translate_net_status_to_container_status(net_status); + p_ctx->status = status; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_net_open_socket(VC_CONTAINER_IO_T *ctx, + VC_CONTAINER_IO_MODE_T mode, bool is_udp) +{ + VC_CONTAINER_IO_MODULE_T *module = ctx->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + const char *host, *port; + + /* Treat empty host or port strings as not defined */ + port = vc_uri_port(ctx->uri_parts); + if (port && !*port) + port = NULL; + + /* Require the port to be defined */ + if (!port) { status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; } + + host = vc_uri_host(ctx->uri_parts); + if (host && !*host) + host = NULL; + + if (!host) + { + /* TCP servers cannot be handled by this interface and UDP senders need a target */ + if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE) + { + status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; + goto error; + } + } + + module->sock = vc_container_net_open(host, port, is_udp ? 0 : VC_CONTAINER_NET_OPEN_FLAG_STREAM, NULL); + if (!module->sock) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } + +#ifdef IO_NET_CAPTURE_PACKETS + if (!is_udp || mode == VC_CONTAINER_IO_MODE_READ) + module->read_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_READ); + if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE) + module->write_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_WRITE); +#endif + +error: + return status; +} + +/***************************************************************************** +Functions exported as part of the I/O Module API + *****************************************************************************/ + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *p_ctx, + const char *unused, VC_CONTAINER_IO_MODE_T mode ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = 0; + bool is_udp; + VC_CONTAINER_PARAM_UNUSED(unused); + + if (!io_net_recognise_scheme(p_ctx->uri, &is_udp)) + { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } + + module = (VC_CONTAINER_IO_MODULE_T *)malloc( sizeof(*module) ); + if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->module = module; + + status = io_net_open_socket(p_ctx, mode, is_udp); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + p_ctx->pf_close = io_net_close; + p_ctx->pf_read = io_net_read; + p_ctx->pf_write = io_net_write; + p_ctx->pf_control = io_net_control; + + /* Disable caching, as this will block waiting for enough data to fill the cache or an error */ + p_ctx->capabilities = VC_CONTAINER_IO_CAPS_CANT_SEEK; + + return VC_CONTAINER_SUCCESS; + +error: + io_net_close(p_ctx); + return status; +} diff --git a/containers/io/io_null.c b/containers/io/io_null.c new file mode 100755 index 0000000..cd83bb8 --- /dev/null +++ b/containers/io/io_null.c @@ -0,0 +1,91 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_uri.h" + +VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *, const char *, + VC_CONTAINER_IO_MODE_T ); + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_null_close( VC_CONTAINER_IO_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_null_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(buffer); + VC_CONTAINER_PARAM_UNUSED(size); + return size; +} + +/*****************************************************************************/ +static size_t io_null_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(buffer); + VC_CONTAINER_PARAM_UNUSED(size); + return size; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_null_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(offset); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *p_ctx, + const char *unused, VC_CONTAINER_IO_MODE_T mode ) +{ + VC_CONTAINER_PARAM_UNUSED(unused); + VC_CONTAINER_PARAM_UNUSED(mode); + + /* Check the URI */ + if (!vc_uri_scheme(p_ctx->uri_parts) || + (strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "null") && + strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "null"))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + p_ctx->pf_close = io_null_close; + p_ctx->pf_read = io_null_read; + p_ctx->pf_write = io_null_write; + p_ctx->pf_seek = io_null_seek; + return VC_CONTAINER_SUCCESS; +} diff --git a/containers/io/io_pktfile.c b/containers/io/io_pktfile.c new file mode 100755 index 0000000..7f4defb --- /dev/null +++ b/containers/io/io_pktfile.c @@ -0,0 +1,261 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_io.h" +#include "containers/core/containers_uri.h" + +/** Native byte order word */ +#define NATIVE_BYTE_ORDER 0x50415753 +/** Reverse of native byte order - need to swap bytes around */ +#define SWAP_BYTE_ORDER 0x53574150 + +typedef struct VC_CONTAINER_IO_MODULE_T +{ + FILE *stream; + bool is_native_order; +} VC_CONTAINER_IO_MODULE_T; + +/** List of recognised schemes. + * Note: always use lower case for the scheme name. */ +static const char * recognised_schemes[] = { + "rtp", "rtppkt", "rtsp", "rtsppkt", "pktfile", +}; + +VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *, const char *, + VC_CONTAINER_IO_MODE_T ); + +/*****************************************************************************/ +static bool recognise_scheme(const char *scheme) +{ + size_t ii; + + if (!scheme) + return false; + + for (ii = 0; ii < countof(recognised_schemes); ii++) + { + if (strcmp(recognised_schemes[ii], scheme) == 0) + return true; + } + + return false; +} + +/*****************************************************************************/ +static uint32_t swap_byte_order( uint32_t value ) +{ + /* Reverse the order of the bytes in the word */ + return ((value << 24) | ((value & 0xFF00) << 8) | ((value >> 8) & 0xFF00) | (value >> 24)); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T io_pktfile_close( VC_CONTAINER_IO_T *p_ctx ) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + fclose(module->stream); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t io_pktfile_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + VC_CONTAINER_IO_MODULE_T *module = p_ctx->module; + uint32_t length = 0; + size_t ret; + + ret = fread(&length, 1, sizeof(length), module->stream); + if (ret != sizeof(length)) + { + if( feof(module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; + else p_ctx->status = VC_CONTAINER_ERROR_FAILED; + return 0; + } + + if (!module->is_native_order) + length = swap_byte_order(length); + + if (length > 1<<20) + { + p_ctx->status = VC_CONTAINER_ERROR_FAILED; + return 0; + } + + if (size > length) + size = length; + ret = fread(buffer, 1, size, module->stream); + if(ret != size) + { + if( feof(module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; + else p_ctx->status = VC_CONTAINER_ERROR_FAILED; + } + else if (length > size) + { + /* Not enough space to read all the packet, so skip to the next one. */ + length -= size; + vc_container_assert((long)length > 0); + fseek(module->stream, (long)length, SEEK_CUR); + } + + return ret; +} + +/*****************************************************************************/ +static size_t io_pktfile_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size) +{ + uint32_t size_word; + size_t ret; + + if (size >= 0xFFFFFFFFUL) + size_word = 0xFFFFFFFFUL; + else + size_word = (uint32_t)size; + + ret = fwrite(&size_word, 1, sizeof(size_word), p_ctx->module->stream); + if (ret != sizeof(size_word)) + { + p_ctx->status = VC_CONTAINER_ERROR_FAILED; + return 0; + } + + ret = fwrite(buffer, 1, size_word, p_ctx->module->stream); + if (ret != size_word) + p_ctx->status = VC_CONTAINER_ERROR_FAILED; + if (fflush(p_ctx->module->stream) != 0) + p_ctx->status = VC_CONTAINER_ERROR_FAILED; + + return ret; +} + +/*****************************************************************************/ +static FILE *open_file(VC_CONTAINER_IO_T *ctx, VC_CONTAINER_IO_MODE_T mode, + VC_CONTAINER_STATUS_T *p_status) +{ + const char *psz_mode = mode == VC_CONTAINER_IO_MODE_WRITE ? "wb+" : "rb"; + FILE *stream = 0; + const char *port, *path; + + /* Treat empty port or path strings as not defined */ + port = vc_uri_port(ctx->uri_parts); + if (port && !*port) + port = NULL; + + path = vc_uri_path(ctx->uri_parts); + if (path && !*path) + path = NULL; + + /* Require the port to be undefined and the path to be defined */ + if (port || !path) { *p_status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; } + + if (!recognise_scheme(vc_uri_scheme(ctx->uri_parts))) + { *p_status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } + + stream = fopen(path, psz_mode); + if(!stream) { *p_status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; } + + *p_status = VC_CONTAINER_SUCCESS; + return stream; + +error: + return NULL; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T write_byte_order(FILE *stream) +{ + /* Simple byte order header word */ + uint32_t value = NATIVE_BYTE_ORDER; + + if (fwrite(&value, 1, sizeof(value), stream) != sizeof(value)) + return VC_CONTAINER_ERROR_OUT_OF_SPACE; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T read_byte_order(FILE *stream, bool *is_native) +{ + uint32_t value; + + if (fread(&value, 1, sizeof(value), stream) != sizeof(value)) + return VC_CONTAINER_ERROR_EOS; + + switch (value) + { + case NATIVE_BYTE_ORDER: *is_native = true; break; + case SWAP_BYTE_ORDER: *is_native = false; break; + default: return VC_CONTAINER_ERROR_CORRUPTED; + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *p_ctx, + const char *unused, VC_CONTAINER_IO_MODE_T mode ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_IO_MODULE_T *module = 0; + FILE *stream = 0; + bool is_native_order = true; + VC_CONTAINER_PARAM_UNUSED(unused); + + stream = open_file(p_ctx, mode, &status); + if (status != VC_CONTAINER_SUCCESS) goto error; + + if (mode == VC_CONTAINER_IO_MODE_WRITE) + status = write_byte_order(stream); + else + status = read_byte_order(stream, &is_native_order); + if (status != VC_CONTAINER_SUCCESS) goto error; + + module = malloc( sizeof(*module) ); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + + p_ctx->module = module; + module->stream = stream; + module->is_native_order = is_native_order; + p_ctx->pf_close = io_pktfile_close; + p_ctx->pf_read = io_pktfile_read; + p_ctx->pf_write = io_pktfile_write; + + /* Do not allow caching by I/O core, as this will merge packets in the cache. */ + p_ctx->capabilities = VC_CONTAINER_IO_CAPS_CANT_SEEK; + return VC_CONTAINER_SUCCESS; + +error: + if(stream) fclose(stream); + return status; +} diff --git a/containers/metadata/id3/CMakeLists.txt b/containers/metadata/id3/CMakeLists.txt new file mode 100755 index 0000000..30178f8 --- /dev/null +++ b/containers/metadata/id3/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_metadata_id3 ${LIBRARY_TYPE} id3_metadata_reader.c) + +target_link_libraries(reader_metadata_id3 containers) + +install(TARGETS reader_metadata_id3 DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/metadata/id3/id3_metadata_reader.c b/containers/metadata/id3/id3_metadata_reader.c new file mode 100755 index 0000000..549273b --- /dev/null +++ b/containers/metadata/id3/id3_metadata_reader.c @@ -0,0 +1,450 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +#include "id3_metadata_strings.h" + +/****************************************************************************** +Defines +******************************************************************************/ +#define ID3_SYNC_SAFE(x) ((((x >> 24) & 0x7f) << 21) | (((x >> 16) & 0x7f) << 14) | \ + (((x >> 8) & 0x7f) << 7) | (((x >> 0) & 0x7f) << 0)) + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static VC_CONTAINER_METADATA_T *id3_metadata_append( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_METADATA_KEY_T key, + unsigned int size ) +{ + VC_CONTAINER_METADATA_T *meta, **p_meta; + unsigned int i; + + for (i = 0; i != p_ctx->meta_num; ++i) + { + if (key == p_ctx->meta[i]->key) break; + } + + /* Avoid duplicate entries for now */ + if (i < p_ctx->meta_num) return NULL; + + /* Sanity check size, truncate if necessary */ + size = MIN(size, 512); + + /* Allocate a new metadata entry */ + if((meta = malloc(sizeof(VC_CONTAINER_METADATA_T) + size)) == NULL) + return NULL; + + /* We need to grow the array holding the metadata entries somehow, ideally, + we'd like to use a linked structure of some sort but realloc is probably + okay in this case */ + if((p_meta = realloc(p_ctx->meta, sizeof(VC_CONTAINER_METADATA_T *) * (p_ctx->meta_num + 1))) == NULL) + { + free(meta); + return NULL; + } + + p_ctx->meta = p_meta; + memset(meta, 0, sizeof(VC_CONTAINER_METADATA_T) + size); + p_ctx->meta[p_ctx->meta_num] = meta; + meta->key = key; + meta->value = (char *)&meta[1]; + meta->size = size; + p_ctx->meta_num++; + + return meta; +} + +/*****************************************************************************/ +static VC_CONTAINER_METADATA_T *id3_read_metadata_entry( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_METADATA_KEY_T key, unsigned int len ) +{ + VC_CONTAINER_METADATA_T *meta; + + if ((meta = id3_metadata_append(p_ctx, key, len + 1)) != NULL) + { + unsigned int size = meta->size - 1; + READ_BYTES(p_ctx, meta->value, size); + + if (len > size) + { + LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size); + SKIP_BYTES(p_ctx, len - size); + } + } + else + { + SKIP_BYTES(p_ctx, len); + } + + return meta; +} + +/*****************************************************************************/ +static VC_CONTAINER_METADATA_T *id3_read_metadata_entry_ex( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_METADATA_KEY_T key, unsigned int len, const char *encoding ) +{ + VC_CONTAINER_METADATA_T *meta; + + if ((meta = id3_metadata_append(p_ctx, key, encoding ? len + 2 : len + 1)) != NULL) + { + unsigned int size; + + if (encoding) + { + size = meta->size - 2; + READ_STRING_UTF16(p_ctx, meta->value, size, "ID3v2 data"); + } + else + { + size = meta->size - 1; + READ_STRING(p_ctx, meta->value, size, "ID3v2 data"); + } + + if (len > size) + { + LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size); + SKIP_BYTES(p_ctx, len - size); + } + } + + return meta; +} + +/*****************************************************************************/ +static VC_CONTAINER_METADATA_T *id3_add_metadata_entry( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_METADATA_KEY_T key, const char *value ) +{ + VC_CONTAINER_METADATA_T *meta; + unsigned int len = strlen(value); + + if ((meta = id3_metadata_append(p_ctx, key, len + 1)) != NULL) + { + unsigned int size = meta->size - 1; + + if (len > size) + { + LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size); + } + + strncpy(meta->value, value, size); + } + + return meta; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T id3_read_id3v2_frame( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_FOURCC_T frame_id, uint32_t frame_size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_METADATA_KEY_T key; + VC_CONTAINER_METADATA_T *meta = NULL; + uint8_t encoding; + const char *charset = NULL; + + if(frame_size < 1) return VC_CONTAINER_ERROR_CORRUPTED; + + switch (frame_id) + { + case VC_FOURCC('T','A','L','B'): key = VC_CONTAINER_METADATA_KEY_ALBUM; break; + case VC_FOURCC('T','I','T','2'): key = VC_CONTAINER_METADATA_KEY_TITLE; break; + case VC_FOURCC('T','R','C','K'): key = VC_CONTAINER_METADATA_KEY_TRACK; break; + case VC_FOURCC('T','P','E','1'): key = VC_CONTAINER_METADATA_KEY_ARTIST; break; + case VC_FOURCC('T','C','O','N'): key = VC_CONTAINER_METADATA_KEY_GENRE; break; + default: key = VC_CONTAINER_METADATA_KEY_UNKNOWN; break; + } + + if (key == VC_CONTAINER_METADATA_KEY_UNKNOWN) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + encoding = READ_U8(p_ctx, "ID3v2 text encoding byte"); + frame_size -= 1; + + switch(encoding) + { + case 0: /* ISO-8859-1 */ + case 3: /* UTF-8 */ + break; + case 1: /* UTF-16 with BOM */ + if(frame_size < 2) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_U16(p_ctx, "ID3v2 text encoding BOM"); /* FIXME: Check BOM, 0xFFFE vs 0xFEFFF */ + frame_size -= 2; + charset = "UTF16-LE"; + break; + case 2: /* UTF-16BE */ + charset = "UTF16-BE"; + break; + default: + LOG_DEBUG(p_ctx, "skipping frame, text encoding %x not supported", encoding); + SKIP_BYTES(p_ctx, frame_size); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + if ((meta = id3_read_metadata_entry_ex(p_ctx, key, frame_size, charset)) != NULL) + { + if (charset) + { + utf8_from_charset(charset, meta->value, meta->size, meta->value, meta->size); + } + + meta->encoding = VC_CONTAINER_CHAR_ENCODING_UTF8; /* Okay for ISO-8859-1 as well? */ + + status = VC_CONTAINER_SUCCESS; + } + else + { + SKIP_BYTES(p_ctx, frame_size); + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T id3_read_id3v2_tag( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint8_t maj_version, flags; + uint32_t tag_size, size = 0; + uint8_t peek_buf[10]; + + SKIP_STRING(p_ctx, 3, "ID3v2 identifier"); + maj_version = READ_U8(p_ctx, "ID3v2 version (major)"); + SKIP_U8(p_ctx, "ID3v2 version (minor)"); + flags = READ_U8(p_ctx, "ID3v2 flags"); + tag_size = READ_U32(p_ctx, "ID3v2 syncsafe tag size"); + tag_size = ID3_SYNC_SAFE(tag_size); + LOG_DEBUG(p_ctx, "ID3v2 tag size: %d", tag_size); + + /* Check that we support this major version */ + if (!(maj_version == 4 || maj_version == 3 || maj_version == 2)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* We can't currently handle unsynchronisation */ + if ((flags >> 7) & 1) + { + LOG_DEBUG(p_ctx, "skipping unsynchronised tag, not supported"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + /* FIXME: check for version 2.2 and extract iTunes gapless playback information */ + if (maj_version == 2) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if ((flags >> 6) & 1) + { + /* Skip extended header, we don't support it */ + uint32_t ext_hdr_size; + LOG_DEBUG(p_ctx, "skipping ID3v2 extended header, not supported"); + ext_hdr_size = READ_U32(p_ctx, "ID3v2 syncsafe extended header size"); + ext_hdr_size = ID3_SYNC_SAFE(ext_hdr_size); + LOG_DEBUG(p_ctx, "ID3v2 extended header size: %d", ext_hdr_size); + SKIP_BYTES(p_ctx, MIN(tag_size, ext_hdr_size)); + size += ext_hdr_size; + } + + while (PEEK_BYTES(p_ctx, peek_buf, 10) == 10 && size < tag_size) + { + VC_CONTAINER_FOURCC_T frame_id; + uint32_t frame_size; + uint8_t format_flags; + + frame_id = READ_FOURCC(p_ctx, "Frame ID"); + frame_size = READ_U32(p_ctx, "Frame Size"); + + if (maj_version >= 4) + { + frame_size = ID3_SYNC_SAFE(frame_size); + LOG_DEBUG(p_ctx, "ID3v2 actual frame size: %d", frame_size); + } + + SKIP_U8(p_ctx, "ID3v2 status message flags"); + format_flags = READ_U8(p_ctx, "ID3v2 format description flags"); + + size += 10; + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS || !frame_id) + break; + + /* Early exit if we detect an invalid tag size */ + if (size + frame_size > tag_size) + { + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + break; + } + + /* We can't currently handle unsynchronised frames */ + if ((format_flags >> 1) & 1) + { + LOG_DEBUG(p_ctx, "skipping unsynchronised frame, not supported"); + SKIP_BYTES(p_ctx, frame_size); + continue; + } + + if ((status = id3_read_id3v2_frame(p_ctx, frame_id, frame_size)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "skipping unsupported frame"); + SKIP_BYTES(p_ctx, frame_size); + } + + size += frame_size; + } + + /* Try to skip to end of tag in case we bailed out early */ + if (size < tag_size) SKIP_BYTES(p_ctx, tag_size - size); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T id3_read_id3v1_tag( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint8_t track, genre; + char track_num[4] = {0}; + + SKIP_STRING(p_ctx, 3, "ID3v1 identifier"); + /* ID3v1 title */ + id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_TITLE, 30); + /* ID3v1 artist */ + id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_ARTIST, 30); + /* ID3v1 album */ + id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_ALBUM, 30); + /* ID3v1 year */ + id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_YEAR, 4); + SKIP_STRING(p_ctx, 28, "ID3v1 comment"); + if (READ_U8(p_ctx, "ID3v1 zero-byte") == 0) + { + track = READ_U8(p_ctx, "ID3v1 track"); + snprintf(track_num, sizeof(track_num) - 1, "%02d", track); + id3_add_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_TRACK, track_num); + } + else + { + SKIP_BYTES(p_ctx, 1); + } + genre = READ_U8(p_ctx, "ID3v1 genre"); + if (genre < countof(id3_genres)) + { + id3_add_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_GENRE, id3_genres[genre]); + } + + status = STREAM_STATUS(p_ctx); + + return status; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T id3_metadata_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + uint8_t peek_buf[10]; + int64_t data_offset; + + if (PEEK_BYTES(p_ctx, peek_buf, 10) != 10) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Initial ID3v2 tag(s), variable size */ + while ((peek_buf[0] == 'I') && (peek_buf[1] == 'D') && (peek_buf[2] == '3')) + { + if ((status = id3_read_id3v2_tag(p_ctx)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "error reading ID3v2 tag (%i)", status); + } + + if (PEEK_BYTES(p_ctx, peek_buf, 10) != 10) break; + } + + data_offset = STREAM_POSITION(p_ctx); + + /* ID3v1 tag, 128 bytes at the end of a file */ + if (p_ctx->priv->io->size >= INT64_C(128) && STREAM_SEEKABLE(p_ctx)) + { + SEEK(p_ctx, p_ctx->priv->io->size - INT64_C(128)); + if (PEEK_BYTES(p_ctx, peek_buf, 3) != 3) goto end; + + if ((peek_buf[0] == 'T') && (peek_buf[1] == 'A') && (peek_buf[2] == 'G')) + { + if ((status = id3_read_id3v1_tag(p_ctx)) != VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "error reading ID3v1 tag (%i)", status); + } + } + } + +end: + /* Restore position to start of data */ + if (STREAM_POSITION(p_ctx) != data_offset) + SEEK(p_ctx, data_offset); + + p_ctx->priv->pf_close = id3_metadata_reader_close; + + if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; + + return VC_CONTAINER_SUCCESS; + +error: + LOG_DEBUG(p_ctx, "error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open id3_metadata_reader_open +#endif diff --git a/containers/metadata/id3/id3_metadata_strings.h b/containers/metadata/id3/id3_metadata_strings.h new file mode 100755 index 0000000..1c953d9 --- /dev/null +++ b/containers/metadata/id3/id3_metadata_strings.h @@ -0,0 +1,179 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* ID3 genre byte translation table */ +static const char* id3_genres[] = +{ + "Blues", + "Classic Rock", + "Country", + "Dance", + "Disco", + "Funk", + "Grunge", + "Hip-Hop", + "Jazz", + "Metal", + "New Age", + "Oldies", + "Other", + "Pop", + "R&B", + "Rap", + "Reggae", + "Rock", + "Techno", + "Industrial", + "Alternative", + "Ska", + "Death Metal", + "Pranks", + "Soundtrack", + "Euro-Techno", + "Ambient", + "Trip-Hop", + "Vocal", + "Jazz+Funk", + "Fusion", + "Trance", + "Classical", + "Instrumental", + "Acid", + "House", + "Game", + "Sound Clip", + "Gospel", + "Noise", + "Alternative Rock", + "Bass", + "Soul", + "Punk", + "Space", + "Meditative", + "Instrumental Pop", + "Instrumental Rock", + "Ethnic", + "Gothic", + "Darkwave", + "Techno-Industrial", + "Electronic", + "Pop-Folk", + "Eurodance", + "Dream", + "Southern Rock", + "Comedy", + "Cult", + "Gangsta", + "Top 40", + "Christian Rap", + "Pop/Funk", + "Jungle", + "Native American", + "Cabaret", + "New Wave", + "Psychadelic", + "Rave", + "Showtunes", + "Trailer", + "Lo-Fi", + "Tribal", + "Acid Punk", + "Acid Jazz", + "Polka", + "Retro", + "Musical", + "Rock & Roll", + "Hard Rock", + "Folk", + "Folk-Rock", + "National Folk", + "Swing", + "Fast Fusion", + "Bebob", + "Latin", + "Revival", + "Celtic", + "Bluegrass", + "Avantgarde", + "Gothic Rock", + "Progressive Rock", + "Psychedelic Rock", + "Symphonic Rock", + "Slow Rock", + "Big Band", + "Chorus", + "Easy Listening", + "Acoustic", + "Humour", + "Speech", + "Chanson", + "Opera", + "Chamber Music", + "Sonata", + "Symphony", + "Booty Bass", + "Primus", + "Porn Groove", + "Satire", + "Slow Jam", + "Club", + "Tango", + "Samba", + "Folklore", + "Ballad", + "Power Ballad", + "Rhythmic Soul", + "Freestyle", + "Duet", + "Punk Rock", + "Drum Solo", + "A capella", + "Euro-House", + "Dance Hall", + "Goa", + "Drum & Bass", + "Club-House", + "Hardcore", + "Terror", + "Indie", + "BritPop", + "Negerpunk", + "Polsk Punk", + "Beat", + "Christian Gangsta Rap", + "Heavy Metal", + "Black Metal", + "Crossover", + "Contemporary Christian", + "Christian Rock", + "Merengue", + "Salsa", + "Thrash Metal", + "Anime", + "JPop", + "SynthPop" +}; diff --git a/containers/mkv/CMakeLists.txt b/containers/mkv/CMakeLists.txt new file mode 100755 index 0000000..0c31a30 --- /dev/null +++ b/containers/mkv/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_mkv ${LIBRARY_TYPE} matroska_reader.c) + +target_link_libraries(reader_mkv containers) + +install(TARGETS reader_mkv DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/mkv/matroska_reader.c b/containers/mkv/matroska_reader.c new file mode 100755 index 0000000..8625d0c --- /dev/null +++ b/containers/mkv/matroska_reader.c @@ -0,0 +1,2323 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +//#define ENABLE_MKV_EXTRA_LOGGING +#define CONTAINER_IS_BIG_ENDIAN +#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->element_level +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define MKV_TRACKS_MAX 16 +#define MKV_CODECID_MAX 32 +#define MKV_MAX_LACING_NUM 64 + +#define MKV_MAX_ENCODINGS 1 +#define MKV_MAX_ENCODING_DATA 256 + +#define MKV_MAX_ELEMENT_LEVEL 8 +#define MKV_MAX_CONSECUTIVE_UNKNOWN_ELEMENTS 5 +#define MKV_MAX_ELEMENT_SIZE (1<<29) /* Does not apply to the data element */ +#define MKV_MAX_STRING_SIZE 256 +#define MKV_ELEMENT_MIN_HEADER_SIZE 2 + +#define MKV_MAX_READER_STATE_LEVEL 4 + +#define MKV_SKIP_U8(ctx,n) (size -= 1, SKIP_U8(ctx,n)) +#define MKV_SKIP_U16(ctx,n) (size -= 2, SKIP_U16(ctx,n)) +#define MKV_SKIP_U24(ctx,n) (size -= 3, SKIP_U24(ctx,n)) +#define MKV_SKIP_U32(ctx,n) (size -= 4, SKIP_U32(ctx,n)) +#define MKV_SKIP_U64(ctx,n) (size -= 8, SKIP_U64(ctx,n)) +#define MKV_READ_U8(ctx,n) (size -= 1, READ_U8(ctx,n)) +#define MKV_READ_U16(ctx,n) (size -= 2, READ_U16(ctx,n)) +#define MKV_READ_U24(ctx,n) (size -= 3, READ_U24(ctx,n)) +#define MKV_READ_U32(ctx,n) (size -= 4, READ_U32(ctx,n)) +#define MKV_READ_U64(ctx,n) (size -= 8, READ_U64(ctx,n)) +#define MKV_READ_BYTES(ctx,buffer,sz) (size -= sz, READ_BYTES(ctx,buffer,sz)) +#define MKV_SKIP_BYTES(ctx,sz) (size -= sz, SKIP_BYTES(ctx,sz)) + +#define CHECK_POINT(a) do { \ + /*if(size < 0 && size != INT64_C(-1)) return VC_CONTAINER_ERROR_CORRUPTED;*/ \ + if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); } while(0) + +static uint32_t mkv_io_read_id(VC_CONTAINER_IO_T *io, int64_t *size) +{ + uint32_t value, mask; + + value = vc_container_io_read_uint8(io); (*size)--; + for(mask = 0x80; mask; mask <<= 7) + { + if(value & mask) return value; + value = (value << 8) | vc_container_io_read_uint8(io); (*size)--; + } + return 0; +} + +static int64_t mkv_io_read_uint(VC_CONTAINER_IO_T *io, int64_t *size) +{ + uint64_t value, mask; + + value = vc_container_io_read_uint8(io); (*size)--; + if(value == 0xFF) return -1; + + for(mask = 0x80; mask; mask <<= 7) + { + if(value & mask) return value & ~mask; + value = (value << 8) | vc_container_io_read_uint8(io); (*size)--; + } + return 0; +} + +static int64_t mkv_io_read_sint(VC_CONTAINER_IO_T *io, int64_t *size) +{ + int64_t value, count = io->offset; + value = mkv_io_read_uint(io, size); + count = io->offset - count; + + switch(count) + { + case 1: value -= 0x3F; break; + case 2: value -= 0x1FFF; break; + case 3: value -= 0xFFFFF; break; + case 4: value -= 0x7FFFFFF; break; + default: break; + } + return value; +} + +#define MKV_READ_ID(ctx, n) mkv_io_read_id((ctx)->priv->io, &size) +#define MKV_READ_UINT(ctx, n) mkv_io_read_uint((ctx)->priv->io, &size) +#define MKV_READ_SINT(ctx, n) mkv_io_read_sint((ctx)->priv->io, &size) + +/****************************************************************************** +Type definitions. +******************************************************************************/ + +typedef enum +{ + MKV_ELEMENT_ID_UNKNOWN = 0, + + /* EBML Basics */ + MKV_ELEMENT_ID_EBML = 0x1A45DFA3, + MKV_ELEMENT_ID_EBML_VERSION = 0x4286, + MKV_ELEMENT_ID_EBML_READ_VERSION = 0x42F7, + MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH = 0x42F2, + MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH = 0x42F3, + MKV_ELEMENT_ID_DOCTYPE = 0x4282, + MKV_ELEMENT_ID_DOCTYPE_VERSION = 0x4287, + MKV_ELEMENT_ID_DOCTYPE_READ_VERSION = 0x4285, + + /* Global Elements */ + MKV_ELEMENT_ID_CRC32 = 0xBF, + MKV_ELEMENT_ID_VOID = 0xEC, + + /* Segment */ + MKV_ELEMENT_ID_SEGMENT = 0x18538067, + + /* Meta Seek Information */ + MKV_ELEMENT_ID_SEEK_HEAD = 0x114D9B74, + MKV_ELEMENT_ID_SEEK = 0x4DBB, + MKV_ELEMENT_ID_SEEK_ID = 0x53AB, + MKV_ELEMENT_ID_SEEK_POSITION = 0x53AC, + + /* Segment Information */ + MKV_ELEMENT_ID_INFO = 0x1549A966, + MKV_ELEMENT_ID_SEGMENT_UID = 0x73A4, + MKV_ELEMENT_ID_SEGMENT_FILENAME = 0x7384, + MKV_ELEMENT_ID_PREV_UID = 0x3CB923, + MKV_ELEMENT_ID_PREV_FILENAME = 0x3C83AB, + MKV_ELEMENT_ID_NEXT_UID = 0x3EB923, + MKV_ELEMENT_ID_NEXT_FILENAME = 0x3E83BB, + MKV_ELEMENT_ID_SEGMENT_FAMILY = 0x4444, + MKV_ELEMENT_ID_CHAPTER_TRANSLATE = 0x6924, + MKV_ELEMENT_ID_CHAPTER_TRANSLATE_EDITION_UID = 0x69FC, + MKV_ELEMENT_ID_CHAPTER_TRANSLATE_CODEC = 0x69BF, + MKV_ELEMENT_ID_CHAPTER_TRANSLATE_ID = 0x69A5, + MKV_ELEMENT_ID_TIMECODE_SCALE = 0x2AD7B1, + MKV_ELEMENT_ID_DURATION = 0x4489, + MKV_ELEMENT_ID_DATE_UTC = 0x4461, + MKV_ELEMENT_ID_TITLE = 0x7BA9, + MKV_ELEMENT_ID_MUXING_APP = 0x4D80, + MKV_ELEMENT_ID_WRITING_APP = 0x5741, + + /* Cluster */ + MKV_ELEMENT_ID_CLUSTER = 0x1F43B675, + MKV_ELEMENT_ID_TIMECODE = 0xE7, + MKV_ELEMENT_ID_SILENT_TRACKS = 0x5854, + MKV_ELEMENT_ID_SILENT_TRACK_NUMBER = 0x58D7, + MKV_ELEMENT_ID_POSITION = 0xA7, + MKV_ELEMENT_ID_PREV_SIZE = 0xAB, + MKV_ELEMENT_ID_BLOCKGROUP = 0xA0, + MKV_ELEMENT_ID_BLOCK = 0xA1, + MKV_ELEMENT_ID_BLOCK_ADDITIONS = 0x75A1, + MKV_ELEMENT_ID_BLOCK_MORE = 0xA6, + MKV_ELEMENT_ID_BLOCK_ADD_ID = 0xEE, + MKV_ELEMENT_ID_BLOCK_ADDITIONAL = 0xA5, + MKV_ELEMENT_ID_BLOCK_DURATION = 0x9B, + MKV_ELEMENT_ID_REFERENCE_PRIORITY = 0xFA, + MKV_ELEMENT_ID_REFERENCE_BLOCK = 0xFB, + MKV_ELEMENT_ID_CODEC_STATE = 0xA4, + MKV_ELEMENT_ID_SLICES = 0x8E, + MKV_ELEMENT_ID_TIME_SLICE = 0xE8, + MKV_ELEMENT_ID_LACE_NUMBER = 0xCC, + MKV_ELEMENT_ID_SIMPLE_BLOCK = 0xA3, + + /* Track */ + MKV_ELEMENT_ID_TRACKS = 0x1654AE6B, + MKV_ELEMENT_ID_TRACK_ENTRY = 0xAE, + MKV_ELEMENT_ID_TRACK_NUMBER = 0xD7, + MKV_ELEMENT_ID_TRACK_UID = 0x73C5, + MKV_ELEMENT_ID_TRACK_TYPE = 0x83, + MKV_ELEMENT_ID_FLAG_ENABLED = 0xB9, + MKV_ELEMENT_ID_FLAG_DEFAULT = 0x88, + MKV_ELEMENT_ID_FLAG_FORCED = 0x55AA, + MKV_ELEMENT_ID_FLAG_LACING = 0x9C, + MKV_ELEMENT_ID_MIN_CACHE = 0x6DE7, + MKV_ELEMENT_ID_MAX_CACHE = 0x6DF8, + MKV_ELEMENT_ID_DEFAULT_DURATION = 0x23E383, + MKV_ELEMENT_ID_TRACK_TIMECODE_SCALE = 0x23314F, + MKV_ELEMENT_ID_MAX_BLOCK_ADDITION_ID = 0x55EE, + MKV_ELEMENT_ID_NAME = 0x536E, + MKV_ELEMENT_ID_LANGUAGE = 0x22B59C, + MKV_ELEMENT_ID_TRACK_CODEC_ID = 0x86, + MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE = 0x63A2, + MKV_ELEMENT_ID_TRACK_CODEC_NAME = 0x258688, + MKV_ELEMENT_ID_ATTACHMENT_LINK = 0x7446, + MKV_ELEMENT_ID_CODEC_DECODE_ALL = 0xAA, + MKV_ELEMENT_ID_TRACK_OVERLAY = 0x6FAB, + MKV_ELEMENT_ID_TRACK_TRANSLATE = 0x6624, + MKV_ELEMENT_ID_TRACK_TRANSLATE_EDITION_UID = 0x66FC, + MKV_ELEMENT_ID_TRACK_TRANSLATE_CODEC = 0x66BF, + MKV_ELEMENT_ID_TRACK_TRANSLATE_TRACK_ID = 0x66A5, + + /* Video */ + MKV_ELEMENT_ID_VIDEO = 0xE0, + MKV_ELEMENT_ID_FLAG_INTERLACED = 0x9A, + MKV_ELEMENT_ID_STEREO_MODE = 0x53B8, + MKV_ELEMENT_ID_PIXEL_WIDTH = 0xB0, + MKV_ELEMENT_ID_PIXEL_HEIGHT = 0xBA, + MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM = 0x54AA, + MKV_ELEMENT_ID_PIXEL_CROP_TOP = 0x54BB, + MKV_ELEMENT_ID_PIXEL_CROP_LEFT = 0x54CC, + MKV_ELEMENT_ID_PIXEL_CROP_RIGHT = 0x54DD, + MKV_ELEMENT_ID_DISPLAY_WIDTH = 0x54B0, + MKV_ELEMENT_ID_DISPLAY_HEIGHT = 0x54BA, + MKV_ELEMENT_ID_DISPLAY_UNIT = 0x54B2, + MKV_ELEMENT_ID_ASPECT_RATIO_TYPE = 0x54B3, + MKV_ELEMENT_ID_COLOUR_SPACE = 0x2EB524, + MKV_ELEMENT_ID_FRAME_RATE = 0x2383E3, + + /* Audio */ + MKV_ELEMENT_ID_AUDIO = 0xE1, + MKV_ELEMENT_ID_SAMPLING_FREQUENCY = 0xB5, + MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY = 0x78B5, + MKV_ELEMENT_ID_CHANNELS = 0x9F, + MKV_ELEMENT_ID_BIT_DEPTH = 0x6264, + + /* Content Encoding */ + MKV_ELEMENT_ID_CONTENT_ENCODINGS = 0x6D80, + MKV_ELEMENT_ID_CONTENT_ENCODING = 0x6240, + MKV_ELEMENT_ID_CONTENT_ENCODING_ORDER = 0x5031, + MKV_ELEMENT_ID_CONTENT_ENCODING_SCOPE = 0x5032, + MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE = 0x5033, + MKV_ELEMENT_ID_CONTENT_COMPRESSION = 0x5034, + MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO = 0x4254, + MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS = 0x4255, + MKV_ELEMENT_ID_CONTENT_ENCRYPTION = 0x5035, + MKV_ELEMENT_ID_CONTENT_ENCRYPTION_ALGO = 0x47E1, + MKV_ELEMENT_ID_CONTENT_ENCRYPTION_KEY_ID = 0x47E2, + MKV_ELEMENT_ID_CONTENT_SIGNATURE = 0x47E3, + MKV_ELEMENT_ID_CONTENT_SIGNATURE_KEY_ID = 0x47E4, + MKV_ELEMENT_ID_CONTENT_SIGNATURE_ALGO = 0x47E5, + MKV_ELEMENT_ID_CONTENT_SIGNATURE_HASH_ALGO = 0x47E6, + + /* Cueing Data */ + MKV_ELEMENT_ID_CUES = 0x1C53BB6B, + MKV_ELEMENT_ID_CUE_POINT = 0xBB, + MKV_ELEMENT_ID_CUE_TIME = 0xB3, + MKV_ELEMENT_ID_CUE_TRACK_POSITIONS = 0xB7, + MKV_ELEMENT_ID_CUE_TRACK = 0xF7, + MKV_ELEMENT_ID_CUE_CLUSTER_POSITION = 0xF1, + MKV_ELEMENT_ID_CUE_BLOCK_NUMBER = 0x5378, + + /* Attachments */ + MKV_ELEMENT_ID_ATTACHMENTS = 0x1941A469, + + /* Chapters */ + MKV_ELEMENT_ID_CHAPTERS = 0x1043A770, + + /* Tagging */ + MKV_ELEMENT_ID_TAGS = 0x1254C367, + MKV_ELEMENT_ID_TAG = 0x7373, + MKV_ELEMENT_ID_TAG_TARGETS = 0x63C0, + MKV_ELEMENT_ID_TAG_TARGET_TYPE_VALUE = 0x68CA, + MKV_ELEMENT_ID_TAG_TARGET_TYPE = 0x63CA, + MKV_ELEMENT_ID_TAG_TRACK_UID = 0x63C5, + MKV_ELEMENT_ID_TAG_EDITION_UID = 0x63C9, + MKV_ELEMENT_ID_TAG_CHAPTER_UID = 0x63C4, + MKV_ELEMENT_ID_TAG_ATTACHMENT_UID = 0x63C6, + MKV_ELEMENT_ID_TAG_SIMPLE_TAG = 0x67C8, + MKV_ELEMENT_ID_TAG_NAME = 0x45A3, + MKV_ELEMENT_ID_TAG_LANGUAGE = 0x447A, + MKV_ELEMENT_ID_TAG_DEFAULT = 0x4484, + MKV_ELEMENT_ID_TAG_STRING = 0x4487, + MKV_ELEMENT_ID_TAG_BINARY = 0x4485, + + MKV_ELEMENT_ID_INVALID = 0xFFFFFFFF +} MKV_ELEMENT_ID_T; + +/** Context for our reader + */ + +typedef struct +{ + unsigned int track; + unsigned int flags; + int64_t pts; + int64_t cluster_timecode; + int64_t prev_cluster_size; /* Size of the previous cluster if available */ + int64_t frame_duration; + + int level; + struct { + int64_t offset; + int64_t data_start; + int64_t data_offset; + int64_t size; + MKV_ELEMENT_ID_T id; + } levels[MKV_MAX_READER_STATE_LEVEL]; + + bool eos; + bool corrupted; + bool seen_ref_block; + + uint32_t lacing_num_frames; + uint32_t lacing_size; + uint16_t lacing_sizes[MKV_MAX_LACING_NUM]; + uint32_t lacing_current_size; + + /* For header stripping compression */ + uint32_t header_size; + uint8_t *header_data; + uint32_t header_size_backup; +} MKV_READER_STATE_T; + +typedef struct +{ + const MKV_ELEMENT_ID_T id; + const MKV_ELEMENT_ID_T parent_id; + const char *psz_name; + VC_CONTAINER_STATUS_T (*pf_func)(VC_CONTAINER_T *, MKV_ELEMENT_ID_T, int64_t); + +} MKV_ELEMENT_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + MKV_READER_STATE_T *state; + MKV_READER_STATE_T track_state; + + /* Information extracted from the track entry */ + uint32_t number; + uint32_t type; + int64_t timecode_scale; + uint32_t duration; + int64_t frame_duration; + char codecid[MKV_CODECID_MAX]; + + union { + /* video specific */ + struct { + unsigned int interlaced:1; + unsigned int stereo_mode:2; + uint32_t pixel_width; + uint32_t pixel_height; + uint32_t pixel_crop_bottom; + uint32_t pixel_crop_top; + uint32_t pixel_crop_left; + uint32_t pixel_crop_right; + uint32_t display_width; + uint32_t display_height; + uint32_t display_unit; + uint32_t aspect_ratio_type; + float frame_rate; + } video; + + /* audio specific */ + struct { + uint32_t sampling_frequency; + uint32_t output_sampling_frequency; + uint32_t channels; + uint32_t bit_depth; + } audio; + } es_type; + + /* content encoding (i.e. lossless compression and encryption) */ + unsigned int encodings_num; + struct { + enum { + MKV_CONTENT_ENCODING_COMPRESSION_ZLIB, + MKV_CONTENT_ENCODING_COMPRESSION_HEADER, + MKV_CONTENT_ENCODING_ENCRYPTION, + MKV_CONTENT_ENCODING_UNKNOWN + } type; + unsigned int data_size; + uint8_t *data; + } encodings[MKV_MAX_ENCODINGS]; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + MKV_ELEMENT_T *elements_list; + int element_level; + MKV_ELEMENT_ID_T parent_id; + + uint64_t element_offset; /**< Offset to the start of the current element */ + + uint64_t segment_offset; /**< Offset to the start of the data packets */ + int64_t segment_size; + + int tracks_num; + VC_CONTAINER_TRACK_T *tracks[MKV_TRACKS_MAX]; + + MKV_READER_STATE_T state; + int64_t timecode_scale; + float duration; + + uint64_t cluster_offset; /**< Offset to the first cluster */ + uint64_t cues_offset; /**< Offset to the start of the seeking cues */ + uint64_t tags_offset; /**< Offset to the start of the tags */ + + /* + * Variables only used during parsing of the header + */ + + VC_CONTAINER_TRACK_T *parsing; /**< Current track being parsed */ + bool is_doctype_valid; + + MKV_ELEMENT_ID_T seekhead_elem_id; + int64_t seekhead_elem_offset; + + /* Cues */ + unsigned int cue_track; + int64_t cue_timecode; + uint64_t cue_cluster_offset; + unsigned int cue_block; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T mkv_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Prototypes for local functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T mkv_read_element( VC_CONTAINER_T *p_ctx, int64_t size, MKV_ELEMENT_ID_T parent_id ); +static VC_CONTAINER_STATUS_T mkv_read_elements( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_element_data_uint( VC_CONTAINER_T *p_ctx, int64_t size, uint64_t *value ); +static VC_CONTAINER_STATUS_T mkv_read_element_data_float( VC_CONTAINER_T *p_ctx, int64_t size, double *value ); +static VC_CONTAINER_STATUS_T mkv_read_element_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_element_segment( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_element_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_video( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_audio( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_info( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_element_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_compression( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_seek_head( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_element_cues( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); +static VC_CONTAINER_STATUS_T mkv_read_subelements_cue_point( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); + +static VC_CONTAINER_STATUS_T mkv_read_subelements_cluster( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ); + +/****************************************************************************** +List of element IDs and their associated processing functions +******************************************************************************/ +MKV_ELEMENT_T mkv_elements_list[] = +{ + /* EBML Basics */ + {MKV_ELEMENT_ID_EBML, MKV_ELEMENT_ID_UNKNOWN, "EBML", mkv_read_element_ebml}, + {MKV_ELEMENT_ID_EBML_VERSION, MKV_ELEMENT_ID_EBML, "EBMLVersion", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_EBML_READ_VERSION, MKV_ELEMENT_ID_EBML, "EBMLReadVersion", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH, MKV_ELEMENT_ID_EBML, "EBMLMaxIDLength", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH, MKV_ELEMENT_ID_EBML, "EBMLMaxSizeLength", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_DOCTYPE, MKV_ELEMENT_ID_EBML, "DocType", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_DOCTYPE_VERSION, MKV_ELEMENT_ID_EBML, "DocTypeVersion", mkv_read_subelements_ebml}, + {MKV_ELEMENT_ID_DOCTYPE_READ_VERSION, MKV_ELEMENT_ID_EBML, "DocTypeReadVersion", mkv_read_subelements_ebml}, + + /* Global Elements */ + {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, + {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, + + /* Segment */ + {MKV_ELEMENT_ID_SEGMENT, MKV_ELEMENT_ID_UNKNOWN, "Segment", mkv_read_element_segment}, + + /* Meta Seek Information */ + {MKV_ELEMENT_ID_SEEK_HEAD, MKV_ELEMENT_ID_SEGMENT, "SeekHead", mkv_read_elements}, + {MKV_ELEMENT_ID_SEEK, MKV_ELEMENT_ID_SEEK_HEAD, "Seek", mkv_read_subelements_seek_head}, + {MKV_ELEMENT_ID_SEEK_ID, MKV_ELEMENT_ID_SEEK, "SeekID", mkv_read_subelements_seek_head}, + {MKV_ELEMENT_ID_SEEK_POSITION, MKV_ELEMENT_ID_SEEK, "SeekPosition", mkv_read_subelements_seek_head}, + + /* Segment Information */ + {MKV_ELEMENT_ID_INFO, MKV_ELEMENT_ID_SEGMENT, "Info", mkv_read_elements}, + {MKV_ELEMENT_ID_SEGMENT_UID, MKV_ELEMENT_ID_INFO, "SegmentUID", 0}, + {MKV_ELEMENT_ID_SEGMENT_FILENAME, MKV_ELEMENT_ID_INFO, "SegmentFilename", 0}, + {MKV_ELEMENT_ID_PREV_UID, MKV_ELEMENT_ID_INFO, "PrevUID", 0}, + {MKV_ELEMENT_ID_PREV_FILENAME, MKV_ELEMENT_ID_INFO, "PrevFilename", 0}, + {MKV_ELEMENT_ID_NEXT_UID, MKV_ELEMENT_ID_INFO, "NextUID", 0}, + {MKV_ELEMENT_ID_NEXT_FILENAME, MKV_ELEMENT_ID_INFO, "NextFilename", 0}, + {MKV_ELEMENT_ID_SEGMENT_FAMILY, MKV_ELEMENT_ID_INFO, "SegmentFamily", 0}, + {MKV_ELEMENT_ID_CHAPTER_TRANSLATE, MKV_ELEMENT_ID_INFO, "ChapterTranslate", 0}, + {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_EDITION_UID, MKV_ELEMENT_ID_INFO, "ChapterTranslateEditionUID", 0}, + {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_CODEC, MKV_ELEMENT_ID_INFO, "ChapterTranslateCodec", 0}, + {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_ID, MKV_ELEMENT_ID_INFO, "ChapterTranslateID", 0}, + {MKV_ELEMENT_ID_TIMECODE_SCALE, MKV_ELEMENT_ID_INFO, "TimecodeScale", mkv_read_subelements_info}, + {MKV_ELEMENT_ID_DURATION, MKV_ELEMENT_ID_INFO, "Duration", mkv_read_subelements_info}, + {MKV_ELEMENT_ID_DATE_UTC, MKV_ELEMENT_ID_INFO, "DateUTC", 0}, + {MKV_ELEMENT_ID_TITLE, MKV_ELEMENT_ID_INFO, "Title", mkv_read_subelements_info}, + {MKV_ELEMENT_ID_MUXING_APP, MKV_ELEMENT_ID_INFO, "MuxingApp", mkv_read_subelements_info}, + {MKV_ELEMENT_ID_WRITING_APP, MKV_ELEMENT_ID_INFO, "WritingApp", mkv_read_subelements_info}, + + /* Cluster */ + {MKV_ELEMENT_ID_CLUSTER, MKV_ELEMENT_ID_SEGMENT, "Cluster", 0}, + {MKV_ELEMENT_ID_TIMECODE, MKV_ELEMENT_ID_CLUSTER, "Timecode", 0}, + {MKV_ELEMENT_ID_SILENT_TRACKS, MKV_ELEMENT_ID_CLUSTER, "SilentTracks", 0}, + {MKV_ELEMENT_ID_SILENT_TRACK_NUMBER, MKV_ELEMENT_ID_CLUSTER, "SilentTrackNumber", 0}, + {MKV_ELEMENT_ID_POSITION, MKV_ELEMENT_ID_CLUSTER, "Position", 0}, + {MKV_ELEMENT_ID_PREV_SIZE, MKV_ELEMENT_ID_CLUSTER, "PrevSize", 0}, + {MKV_ELEMENT_ID_BLOCKGROUP, MKV_ELEMENT_ID_CLUSTER, "BlockGroup", 0}, + {MKV_ELEMENT_ID_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "Block", 0}, + {MKV_ELEMENT_ID_BLOCK_ADDITIONS, MKV_ELEMENT_ID_BLOCKGROUP, "BlockAdditions", 0}, + {MKV_ELEMENT_ID_BLOCK_MORE, MKV_ELEMENT_ID_BLOCK_ADDITIONS, "BlockMore", 0}, + {MKV_ELEMENT_ID_BLOCK_ADD_ID, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAddId", 0}, + {MKV_ELEMENT_ID_BLOCK_ADDITIONAL, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAdditional", 0}, + {MKV_ELEMENT_ID_BLOCK_DURATION, MKV_ELEMENT_ID_BLOCKGROUP, "BlockDuration", 0}, + {MKV_ELEMENT_ID_REFERENCE_PRIORITY, MKV_ELEMENT_ID_BLOCKGROUP, "ReferencePriority", 0}, + {MKV_ELEMENT_ID_REFERENCE_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "ReferenceBlock", 0}, + {MKV_ELEMENT_ID_CODEC_STATE, MKV_ELEMENT_ID_BLOCKGROUP, "CodecState", 0}, + {MKV_ELEMENT_ID_SLICES, MKV_ELEMENT_ID_BLOCKGROUP, "Slices", 0}, + {MKV_ELEMENT_ID_TIME_SLICE, MKV_ELEMENT_ID_SLICES, "TimeSlice", 0}, + {MKV_ELEMENT_ID_LACE_NUMBER, MKV_ELEMENT_ID_TIME_SLICE, "LaceNumber", 0}, + {MKV_ELEMENT_ID_SIMPLE_BLOCK, MKV_ELEMENT_ID_CLUSTER, "SimpleBlock", 0}, + + /* Track */ + {MKV_ELEMENT_ID_TRACKS, MKV_ELEMENT_ID_SEGMENT, "Tracks", mkv_read_elements}, + {MKV_ELEMENT_ID_TRACK_ENTRY, MKV_ELEMENT_ID_TRACKS, "TrackEntry", mkv_read_element_track_entry}, + {MKV_ELEMENT_ID_TRACK_NUMBER, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackNumber", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_UID, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackUID", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_TYPE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackType", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_FLAG_ENABLED, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagEnabled", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_FLAG_DEFAULT, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagDefault", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_FLAG_FORCED, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagForced", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_FLAG_LACING, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagLacing", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_MIN_CACHE, MKV_ELEMENT_ID_TRACK_ENTRY, "MinCache", 0}, + {MKV_ELEMENT_ID_MAX_CACHE, MKV_ELEMENT_ID_TRACK_ENTRY, "MaxCache", 0}, + {MKV_ELEMENT_ID_DEFAULT_DURATION, MKV_ELEMENT_ID_TRACK_ENTRY, "DefaultDuration", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_TIMECODE_SCALE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackTimecodeScale", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_MAX_BLOCK_ADDITION_ID, MKV_ELEMENT_ID_TRACK_ENTRY, "MaxBlockAdditionID", 0}, + {MKV_ELEMENT_ID_NAME, MKV_ELEMENT_ID_TRACK_ENTRY, "Name", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_LANGUAGE, MKV_ELEMENT_ID_TRACK_ENTRY, "Language", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_CODEC_ID, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecID", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecPrivate", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_TRACK_CODEC_NAME, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecName", mkv_read_subelements_track_entry}, + {MKV_ELEMENT_ID_ATTACHMENT_LINK, MKV_ELEMENT_ID_TRACK_ENTRY, "AttachmentLink", 0}, + {MKV_ELEMENT_ID_CODEC_DECODE_ALL, MKV_ELEMENT_ID_TRACK_ENTRY, "DecodeAll", 0}, + {MKV_ELEMENT_ID_TRACK_OVERLAY, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackOverlay", 0}, + {MKV_ELEMENT_ID_TRACK_TRANSLATE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackTranslate", 0}, + {MKV_ELEMENT_ID_TRACK_TRANSLATE_EDITION_UID, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateEditionUID", 0}, + {MKV_ELEMENT_ID_TRACK_TRANSLATE_CODEC, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateCodec", 0}, + {MKV_ELEMENT_ID_TRACK_TRANSLATE_TRACK_ID, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateTrackID", 0}, + + /* Video */ + {MKV_ELEMENT_ID_VIDEO, MKV_ELEMENT_ID_TRACK_ENTRY, "Video", mkv_read_elements}, + {MKV_ELEMENT_ID_FLAG_INTERLACED, MKV_ELEMENT_ID_VIDEO, "FlagInterlaced", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_STEREO_MODE, MKV_ELEMENT_ID_VIDEO, "StereoMode", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_WIDTH, MKV_ELEMENT_ID_VIDEO, "PixelWidth", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_HEIGHT, MKV_ELEMENT_ID_VIDEO, "PixelHeight", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM, MKV_ELEMENT_ID_VIDEO, "PixelCropBottom", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_CROP_TOP, MKV_ELEMENT_ID_VIDEO, "PixelCropTop", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_CROP_LEFT, MKV_ELEMENT_ID_VIDEO, "PixelCropLeft", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_PIXEL_CROP_RIGHT, MKV_ELEMENT_ID_VIDEO, "PixelCropRight", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_DISPLAY_WIDTH, MKV_ELEMENT_ID_VIDEO, "DisplayWidth", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_DISPLAY_HEIGHT, MKV_ELEMENT_ID_VIDEO, "DisplayHeight", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_DISPLAY_UNIT, MKV_ELEMENT_ID_VIDEO, "DisplayUnit", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_ASPECT_RATIO_TYPE, MKV_ELEMENT_ID_VIDEO, "AspectRatioType", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_COLOUR_SPACE, MKV_ELEMENT_ID_VIDEO, "ColourSpace", mkv_read_subelements_video}, + {MKV_ELEMENT_ID_FRAME_RATE, MKV_ELEMENT_ID_VIDEO, "FrameRate", mkv_read_subelements_video}, + + /* Audio */ + {MKV_ELEMENT_ID_AUDIO, MKV_ELEMENT_ID_TRACK_ENTRY, "Audio", mkv_read_elements}, + {MKV_ELEMENT_ID_SAMPLING_FREQUENCY, MKV_ELEMENT_ID_AUDIO, "SamplingFrequency", mkv_read_subelements_audio}, + {MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY, MKV_ELEMENT_ID_AUDIO, "OutputSamplingFrequency", mkv_read_subelements_audio}, + {MKV_ELEMENT_ID_CHANNELS, MKV_ELEMENT_ID_AUDIO, "Channels", mkv_read_subelements_audio}, + {MKV_ELEMENT_ID_BIT_DEPTH, MKV_ELEMENT_ID_AUDIO, "BitDepth", mkv_read_subelements_audio}, + + /* Content Encoding */ + {MKV_ELEMENT_ID_CONTENT_ENCODINGS, MKV_ELEMENT_ID_TRACK_ENTRY, "ContentEncodings", mkv_read_elements}, + {MKV_ELEMENT_ID_CONTENT_ENCODING, MKV_ELEMENT_ID_CONTENT_ENCODINGS, "ContentEncoding", mkv_read_element_encoding}, + {MKV_ELEMENT_ID_CONTENT_ENCODING_ORDER, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingOrder", mkv_read_subelements_encoding}, + {MKV_ELEMENT_ID_CONTENT_ENCODING_SCOPE, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingScope", mkv_read_subelements_encoding}, + {MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingType", mkv_read_subelements_encoding}, + {MKV_ELEMENT_ID_CONTENT_COMPRESSION, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentCompression", mkv_read_elements}, + {MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO, MKV_ELEMENT_ID_CONTENT_COMPRESSION, "ContentCompAlgo", mkv_read_subelements_compression}, + {MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS, MKV_ELEMENT_ID_CONTENT_COMPRESSION, "ContentCompSettings", mkv_read_subelements_compression}, + {MKV_ELEMENT_ID_CONTENT_ENCRYPTION, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncryption", mkv_read_elements}, + {MKV_ELEMENT_ID_CONTENT_ENCRYPTION_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentEncAlgo", 0}, + {MKV_ELEMENT_ID_CONTENT_ENCRYPTION_KEY_ID, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentEncKeyID", 0}, + {MKV_ELEMENT_ID_CONTENT_SIGNATURE, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSignature", 0}, + {MKV_ELEMENT_ID_CONTENT_SIGNATURE_KEY_ID, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigKeyID", 0}, + {MKV_ELEMENT_ID_CONTENT_SIGNATURE_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigAlgo", 0}, + {MKV_ELEMENT_ID_CONTENT_SIGNATURE_HASH_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigHashAlgo", 0}, + + /* Cueing data */ + {MKV_ELEMENT_ID_CUES, MKV_ELEMENT_ID_SEGMENT, "Cues", mkv_read_element_cues}, + {MKV_ELEMENT_ID_CUE_POINT, MKV_ELEMENT_ID_CUES, "Cue Point", mkv_read_elements}, + {MKV_ELEMENT_ID_CUE_TIME, MKV_ELEMENT_ID_CUE_POINT, "Cue Time", 0}, + {MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, MKV_ELEMENT_ID_CUE_POINT, "Cue Track Positions", mkv_read_elements}, + {MKV_ELEMENT_ID_CUE_TRACK, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Track", 0}, + {MKV_ELEMENT_ID_CUE_CLUSTER_POSITION, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Cluster Position", 0}, + {MKV_ELEMENT_ID_CUE_BLOCK_NUMBER, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Block Number", 0}, + + /* Attachments */ + {MKV_ELEMENT_ID_ATTACHMENTS, MKV_ELEMENT_ID_SEGMENT, "Attachments", 0}, + + /* Chapters */ + {MKV_ELEMENT_ID_CHAPTERS, MKV_ELEMENT_ID_SEGMENT, "Chapters", 0}, + + /* Tagging */ + {MKV_ELEMENT_ID_TAGS, MKV_ELEMENT_ID_SEGMENT, "Tags", mkv_read_elements}, + {MKV_ELEMENT_ID_TAG, MKV_ELEMENT_ID_TAGS, "Tag", mkv_read_elements}, + {MKV_ELEMENT_ID_TAG_TARGETS, MKV_ELEMENT_ID_TAG, "Tag Targets", mkv_read_elements}, + {MKV_ELEMENT_ID_TAG_TARGET_TYPE_VALUE, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Target Type Value", 0}, + {MKV_ELEMENT_ID_TAG_TARGET_TYPE, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Target Type", 0}, + {MKV_ELEMENT_ID_TAG_TRACK_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Track UID", 0}, + {MKV_ELEMENT_ID_TAG_EDITION_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Edition UID", 0}, + {MKV_ELEMENT_ID_TAG_CHAPTER_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Chapter UID", 0}, + {MKV_ELEMENT_ID_TAG_ATTACHMENT_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Attachment UID", 0}, + {MKV_ELEMENT_ID_TAG_SIMPLE_TAG, MKV_ELEMENT_ID_TAG, "Simple Tag", mkv_read_elements}, + {MKV_ELEMENT_ID_TAG_NAME, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Name", 0}, + {MKV_ELEMENT_ID_TAG_LANGUAGE, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Language", 0}, + {MKV_ELEMENT_ID_TAG_DEFAULT, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Default", 0}, + {MKV_ELEMENT_ID_TAG_STRING, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag String", 0}, + {MKV_ELEMENT_ID_TAG_BINARY, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Binary", 0}, + + {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} +}; + +MKV_ELEMENT_T mkv_cluster_elements_list[] = +{ + /* Cluster */ + {MKV_ELEMENT_ID_CLUSTER, MKV_ELEMENT_ID_SEGMENT, "Cluster", 0}, + {MKV_ELEMENT_ID_TIMECODE, MKV_ELEMENT_ID_CLUSTER, "Timecode", mkv_read_subelements_cluster}, + {MKV_ELEMENT_ID_SILENT_TRACKS, MKV_ELEMENT_ID_CLUSTER, "SilentTracks", 0}, + {MKV_ELEMENT_ID_SILENT_TRACK_NUMBER, MKV_ELEMENT_ID_CLUSTER, "SilentTrackNumber", 0}, + {MKV_ELEMENT_ID_POSITION, MKV_ELEMENT_ID_CLUSTER, "Position", 0}, + {MKV_ELEMENT_ID_PREV_SIZE, MKV_ELEMENT_ID_CLUSTER, "PrevSize", 0}, + {MKV_ELEMENT_ID_BLOCKGROUP, MKV_ELEMENT_ID_CLUSTER, "BlockGroup", 0}, + {MKV_ELEMENT_ID_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "Block", 0}, + {MKV_ELEMENT_ID_BLOCK_ADDITIONS, MKV_ELEMENT_ID_BLOCKGROUP, "BlockAdditions", 0}, + {MKV_ELEMENT_ID_BLOCK_MORE, MKV_ELEMENT_ID_BLOCK_ADDITIONS, "BlockMore", 0}, + {MKV_ELEMENT_ID_BLOCK_ADD_ID, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAddId", 0}, + {MKV_ELEMENT_ID_BLOCK_ADDITIONAL, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAdditional", 0}, + {MKV_ELEMENT_ID_BLOCK_DURATION, MKV_ELEMENT_ID_BLOCKGROUP, "BlockDuration", mkv_read_subelements_cluster}, + {MKV_ELEMENT_ID_REFERENCE_PRIORITY, MKV_ELEMENT_ID_BLOCKGROUP, "ReferencePriority", 0}, + {MKV_ELEMENT_ID_REFERENCE_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "ReferenceBlock", 0}, + {MKV_ELEMENT_ID_CODEC_STATE, MKV_ELEMENT_ID_BLOCKGROUP, "CodecState", 0}, + {MKV_ELEMENT_ID_SLICES, MKV_ELEMENT_ID_BLOCKGROUP, "Slices", 0}, + {MKV_ELEMENT_ID_TIME_SLICE, MKV_ELEMENT_ID_SLICES, "TimeSlice", 0}, + {MKV_ELEMENT_ID_LACE_NUMBER, MKV_ELEMENT_ID_TIME_SLICE, "LaceNumber", 0}, + {MKV_ELEMENT_ID_SIMPLE_BLOCK, MKV_ELEMENT_ID_CLUSTER, "SimpleBlock", 0}, + + /* Global Elements */ + {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, + {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, + + {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} +}; + +MKV_ELEMENT_T mkv_cue_elements_list[] = +{ + /* Cueing data */ + {MKV_ELEMENT_ID_CUES, MKV_ELEMENT_ID_SEGMENT, "Cues", 0}, + {MKV_ELEMENT_ID_CUE_POINT, MKV_ELEMENT_ID_CUES, "Cue Point", mkv_read_elements}, + {MKV_ELEMENT_ID_CUE_TIME, MKV_ELEMENT_ID_CUE_POINT, "Cue Time", mkv_read_subelements_cue_point}, + {MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, MKV_ELEMENT_ID_CUE_POINT, "Cue Track Positions", mkv_read_elements}, + {MKV_ELEMENT_ID_CUE_TRACK, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Track", mkv_read_subelements_cue_point}, + {MKV_ELEMENT_ID_CUE_CLUSTER_POSITION, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Cluster Position", mkv_read_subelements_cue_point}, + {MKV_ELEMENT_ID_CUE_BLOCK_NUMBER, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Block Number", mkv_read_subelements_cue_point}, + + /* Global Elements */ + {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0}, + {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0}, + + {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0} +}; + +/****************************************************************************** +List of codec mapping +******************************************************************************/ +static const struct { + VC_CONTAINER_FOURCC_T fourcc; + const char *codecid; + VC_CONTAINER_FOURCC_T variant; +} codecid_to_fourcc_table[] = +{ + /* Video */ + {VC_CONTAINER_CODEC_MP1V, "V_MPEG1", 0}, + {VC_CONTAINER_CODEC_MP2V, "V_MPEG2", 0}, + {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/ASP", 0}, + {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/SP", 0}, + {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/AP", 0}, + {VC_CONTAINER_CODEC_DIV3, "V_MPEG4/MS/V3", 0}, + {VC_CONTAINER_CODEC_H264, "V_MPEG4/ISO/AVC", VC_CONTAINER_VARIANT_H264_AVC1}, + {VC_CONTAINER_CODEC_MJPEG, "V_MJPEG", 0}, + {VC_CONTAINER_CODEC_RV10, "V_REAL/RV10", 0}, + {VC_CONTAINER_CODEC_RV20, "V_REAL/RV20", 0}, + {VC_CONTAINER_CODEC_RV30, "V_REAL/RV30", 0}, + {VC_CONTAINER_CODEC_RV40, "V_REAL/RV40", 0}, + {VC_CONTAINER_CODEC_THEORA, "V_THEORA", 0}, + {VC_CONTAINER_CODEC_DIRAC, "V_DIRAC", 0}, + {VC_CONTAINER_CODEC_VP8, "V_VP8", 0}, + + /* Audio */ + {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L3", VC_CONTAINER_VARIANT_MPGA_L3}, + {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L2", VC_CONTAINER_VARIANT_MPGA_L2}, + {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L1", VC_CONTAINER_VARIANT_MPGA_L1}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/MAIN", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/LC", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/SSR", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/LC/SBR", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/MAIN", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/LC", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/SSR", 0}, + {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/LC/SBR", 0}, + {VC_CONTAINER_CODEC_AC3, "A_AC3", 0}, + {VC_CONTAINER_CODEC_EAC3, "A_EAC3", 0}, + {VC_CONTAINER_CODEC_DTS, "A_DTS", 0}, + {VC_CONTAINER_CODEC_MLP, "A_MLP", 0}, + {0, "A_TRUEHD", 0}, + {VC_CONTAINER_CODEC_VORBIS, "A_VORBIS", 0}, + {VC_CONTAINER_CODEC_FLAC, "A_FLAC", 0}, + {VC_CONTAINER_CODEC_PCM_SIGNED_LE, "A_PCM/INT/LIT", 0}, + {VC_CONTAINER_CODEC_PCM_SIGNED_BE, "A_PCM/INT/BIG", 0}, + {VC_CONTAINER_CODEC_PCM_FLOAT_LE, "A_PCM/FLOAT/IEEE", 0}, + {0, "A_REAL/xyzt", 0}, + {0, "A_REAL/14_4", 0}, + + /* Text */ + {VC_CONTAINER_CODEC_TEXT, "S_TEXT/ASCII", 0}, + {VC_CONTAINER_CODEC_TEXT, "S_TEXT/UTF8", 0}, + {VC_CONTAINER_CODEC_SSA, "S_TEXT/ASS", 0}, + {VC_CONTAINER_CODEC_SSA, "S_TEXT/SSA", 0}, + {VC_CONTAINER_CODEC_SSA, "S_ASS", 0}, + {VC_CONTAINER_CODEC_SSA, "S_SSA", 0}, + {VC_CONTAINER_CODEC_USF, "S_TEXT/USF", 0}, + {VC_CONTAINER_CODEC_VOBSUB, "S_VOBSUB", 0}, + + {0, 0} +}; + +/****************************************************************************** +Local Functions +******************************************************************************/ + +static VC_CONTAINER_FOURCC_T mkv_codecid_to_fourcc(const char *codecid, + VC_CONTAINER_FOURCC_T *variant) +{ + unsigned int i; + for(i = 0; codecid_to_fourcc_table[i].codecid; i++) + if(!strcmp(codecid_to_fourcc_table[i].codecid, codecid)) break; + if (variant) *variant = codecid_to_fourcc_table[i].variant; + return codecid_to_fourcc_table[i].fourcc; +} + +#if 0 +/** Find the track associated with an MKV track number */ +static VC_CONTAINER_TRACK_T *mkv_reader_find_track( VC_CONTAINER_T *p_ctx, unsigned int mkv_track_num) +{ + VC_CONTAINER_TRACK_T *p_track = 0; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + if(p_ctx->tracks[i]->priv->module->number == mkv_track_num) break; + + if(i < p_ctx->tracks_num) /* We found it */ + p_track = p_ctx->tracks[i]; + + return p_track; +} +#endif + +/** Base function used to read an MKV/EBML element header. + * This will read the element header do lots of sanity checking and return the element id + * and the size of the data contained in the element */ +static VC_CONTAINER_STATUS_T mkv_read_element_header(VC_CONTAINER_T *p_ctx, int64_t size, + MKV_ELEMENT_ID_T *id, int64_t *element_size, MKV_ELEMENT_ID_T parent_id, + MKV_ELEMENT_T **elem) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + MKV_ELEMENT_T *element; + + module->element_offset = STREAM_POSITION(p_ctx); + + *id = MKV_READ_ID(p_ctx, "Element ID"); + CHECK_POINT(p_ctx); + if(!*id) + { + LOG_DEBUG(p_ctx, "invalid element id %i", *id); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if(elem) element = *elem; + else element = mkv_elements_list; + + /* Find out which Element we are dealing with */ + while(element->id && *id != element->id) element++; + + *element_size = MKV_READ_UINT(p_ctx, "Element Size"); + CHECK_POINT(p_ctx); + LOG_FORMAT(p_ctx, "- Element %s (ID 0x%x), Size: %"PRIi64", Offset: %"PRIi64, + element->psz_name, *id, *element_size, module->element_offset); + + /* Sanity check the element size */ + if(*element_size + 1 < 0 /* Shouldn't ever get that big */ || + /* Only the segment / cluster elements can really be massive */ + (*id != MKV_ELEMENT_ID_SEGMENT && *id != MKV_ELEMENT_ID_CLUSTER && + *element_size > MKV_MAX_ELEMENT_SIZE)) + { + LOG_DEBUG(p_ctx, "element %s has an invalid size (%"PRIi64")", + element->psz_name, *element_size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + if(size >= 0 && *element_size > size) + { + LOG_DEBUG(p_ctx, "element %s is bigger than it should (%"PRIi64" > %"PRIi64")", + element->psz_name, *element_size, size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* Sanity check that the element has the right parent */ + if(element->id && element->parent_id != MKV_ELEMENT_ID_INVALID && + parent_id != MKV_ELEMENT_ID_INVALID && parent_id != element->parent_id) + { + LOG_FORMAT(p_ctx, "Ignoring mis-placed element %s (ID 0x%x)", element->psz_name, *id); + while(element->id != MKV_ELEMENT_ID_UNKNOWN) element++; + } + + /* Sanity check that the element isn't too deeply nested */ + if(module->element_level >= MKV_MAX_ELEMENT_LEVEL) + { + LOG_DEBUG(p_ctx, "element %s is too deep. skipping", element->psz_name); + while(element->id != MKV_ELEMENT_ID_UNKNOWN) element++; + } + + if(elem) *elem = element; + return STREAM_STATUS(p_ctx); +} + +static VC_CONTAINER_STATUS_T mkv_read_element_data(VC_CONTAINER_T *p_ctx, + MKV_ELEMENT_T *element, int64_t element_size, int64_t size) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset; + + offset = STREAM_POSITION(p_ctx); + if (size < 0) size = element_size; + if (size < 0) size = INT64_C(1) << 62; + + /* Call the element specific parsing function */ + if(element->pf_func) + status = element->pf_func(p_ctx, element->id, element_size < 0 ? size : element_size); + + if(status != VC_CONTAINER_SUCCESS) + LOG_DEBUG(p_ctx, "element %s appears to be corrupted (%i)", element->psz_name, status); + + if(element_size < 0) return STREAM_STATUS(p_ctx); /* Unknown size */ + + /* Skip the rest of the element */ + element_size -= (STREAM_POSITION(p_ctx) - offset); + if(element_size < 0) /* Check for overruns */ + { + /* Things have gone really bad here and we ended up reading past the end of the + * element. We could maybe try to be clever and recover by seeking back to the end + * of the element. However if we get there, the file is clearly corrupted so there's + * no guarantee it would work anyway. */ + LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of element %s", + -element_size, element->psz_name); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if(element_size) + LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread in element %s", element_size, element->psz_name); + + if(element_size < MKV_MAX_ELEMENT_SIZE) SKIP_BYTES(p_ctx, element_size); + else SEEK(p_ctx, STREAM_POSITION(p_ctx) + element_size); + + return STREAM_STATUS(p_ctx); +} + +/** Base function used to read an MKV/EBML element. + * This will read the element header do lots of sanity checking and pass on the rest + * of the reading to the element specific reading function */ +static VC_CONTAINER_STATUS_T mkv_read_element(VC_CONTAINER_T *p_ctx, + int64_t size, MKV_ELEMENT_ID_T parent_id) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + MKV_ELEMENT_T *element = p_ctx->priv->module->elements_list; + int64_t element_size; + MKV_ELEMENT_ID_T id; + + status = mkv_read_element_header(p_ctx, size, &id, &element_size, parent_id, &element); + if(status != VC_CONTAINER_SUCCESS) return status; + return mkv_read_element_data(p_ctx, element, element_size, size); +} + +/** Reads an unsigned integer element */ +static VC_CONTAINER_STATUS_T mkv_read_element_data_uint(VC_CONTAINER_T *p_ctx, + int64_t size, uint64_t *value) +{ + switch(size) + { + case 1: *value = READ_U8(p_ctx, "u8-integer"); break; + case 2: *value = READ_U16(p_ctx, "u16-integer"); break; + case 3: *value = READ_U24(p_ctx, "u24-integer"); break; + case 4: *value = READ_U32(p_ctx, "u32-integer"); break; + case 5: *value = READ_U40(p_ctx, "u40-integer"); break; + case 6: *value = READ_U48(p_ctx, "u48-integer"); break; + case 7: *value = READ_U56(p_ctx, "u56-integer"); break; + case 8: *value = READ_U64(p_ctx, "u64-integer"); break; + default: return VC_CONTAINER_ERROR_CORRUPTED; + } + return STREAM_STATUS(p_ctx); +} + +/** Reads a float element */ +static VC_CONTAINER_STATUS_T mkv_read_element_data_float(VC_CONTAINER_T *p_ctx, + int64_t size, double *value) +{ + union { + uint32_t u32; + uint64_t u64; + float f; + double d; + } u; + + switch(size) + { + case 4: u.u32 = READ_U32(p_ctx, "f32-float"); *value = u.f; break; + case 8: u.u64 = READ_U64(p_ctx, "f64-float"); *value = u.d; break; + default: return VC_CONTAINER_ERROR_CORRUPTED; + } + LOG_FORMAT(p_ctx, "float: %f", *value); + return STREAM_STATUS(p_ctx); +} + +/** Reads an MKV EBML element */ +static VC_CONTAINER_STATUS_T mkv_read_element_ebml(VC_CONTAINER_T *p_ctx, + MKV_ELEMENT_ID_T id, int64_t size) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset = STREAM_POSITION(p_ctx); + + /* Read contained elements */ + module->element_level++; + while(status == VC_CONTAINER_SUCCESS && size >= MKV_ELEMENT_MIN_HEADER_SIZE) + { + offset = STREAM_POSITION(p_ctx); + status = mkv_read_element(p_ctx, size, id); + size -= (STREAM_POSITION(p_ctx) - offset); + } + module->element_level--; + return status; +} + +/** Reads the MKV EBML sub-element */ +static VC_CONTAINER_STATUS_T mkv_read_subelements_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint64_t value; + + /* Deal with DocType first since it's a special case */ + if(id == MKV_ELEMENT_ID_DOCTYPE) + { + char doctype[] = "matroska doctype"; + + /* Check we've got the right doctype string for matroska */ + if(size <= 0) goto unknown_doctype; + if(size > (int)sizeof(doctype)) goto unknown_doctype; + if((int)READ_STRING(p_ctx, doctype, size, "string") != size) return STREAM_STATUS(p_ctx); + if((size != sizeof("matroska")-1 || strncmp(doctype, "matroska", (int)size)) && + (size != sizeof("webm")-1 || strncmp(doctype, "webm", (int)size))) + goto unknown_doctype; + + module->is_doctype_valid = true; + return VC_CONTAINER_SUCCESS; + + unknown_doctype: + LOG_DEBUG(p_ctx, "invalid doctype"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + /* The rest are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + case MKV_ELEMENT_ID_EBML_VERSION: + case MKV_ELEMENT_ID_EBML_READ_VERSION: + if(value != 1) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + break; + case MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH: + if(value > 4) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + break; + case MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH: + if(value > 8) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + break; + case MKV_ELEMENT_ID_DOCTYPE_VERSION: + case MKV_ELEMENT_ID_DOCTYPE_READ_VERSION: + default: break; + } + + return STREAM_STATUS(p_ctx); +} + +static VC_CONTAINER_STATUS_T mkv_read_elements( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset = STREAM_POSITION(p_ctx); + bool unknown_size = size < 0; + + /* Read contained elements */ + module->element_level++; + while(status == VC_CONTAINER_SUCCESS && + (unknown_size || size >= MKV_ELEMENT_MIN_HEADER_SIZE)) + { + offset = STREAM_POSITION(p_ctx); + status = mkv_read_element(p_ctx, size, id); + if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset); + } + module->element_level--; + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_element_segment( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset = STREAM_POSITION(p_ctx); + bool unknown_size = size < 0; + + /* Read contained elements */ + /* Initialise state used by reader */ + module->state.level = 0; + module->state.levels[0].offset = STREAM_POSITION(p_ctx); + module->state.levels[0].size = size; + module->state.levels[0].id = MKV_ELEMENT_ID_SEGMENT; + module->state.levels[0].data_start = 0; + module->state.levels[0].data_offset = 0; + module->timecode_scale = 1000000; + module->duration = 0.0; + module->segment_offset = STREAM_POSITION(p_ctx); + module->segment_size = size; + + /* Read contained elements until we have all the information we need to start + * playing the stream */ + module->element_level++; + while(status == VC_CONTAINER_SUCCESS && + (unknown_size || size >= MKV_ELEMENT_MIN_HEADER_SIZE)) + { + MKV_ELEMENT_T *child = mkv_elements_list; + MKV_ELEMENT_ID_T child_id; + int64_t child_size; + + offset = STREAM_POSITION(p_ctx); + + status = mkv_read_element_header(p_ctx, size, &child_id, &child_size, id, &child); + if(status != VC_CONTAINER_SUCCESS) break; + + if(child_id == MKV_ELEMENT_ID_CLUSTER) + { + /* We found the start of the data */ + module->cluster_offset = module->element_offset; + module->state.level = 1; + module->state.levels[1].offset = STREAM_POSITION(p_ctx); + module->state.levels[1].size = child_size; + module->state.levels[1].id = MKV_ELEMENT_ID_CLUSTER; + module->state.levels[1].data_start = 0; + module->state.levels[1].data_offset = 0; + break; + } + + status = mkv_read_element_data(p_ctx, child, child_size, size); + if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset); + } + + module->element_level--; + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_element_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN; + VC_CONTAINER_TRACK_MODULE_T *track_module; + VC_CONTAINER_TRACK_T *track; + VC_CONTAINER_FOURCC_T fourcc = 0, variant = 0; + unsigned int i, extra_size = 0, extra_offset = 0, is_wf = 0, is_bmih = 0; + + /* Allocate and initialise track data */ + if(p_ctx->tracks_num >= MKV_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + module->parsing = track; + track_module = track->priv->module; + track->is_enabled = true; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + track_module->timecode_scale = 1.0; + track_module->es_type.video.frame_rate = 0; + + status = mkv_read_elements( p_ctx, id, size ); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* Sanity check the data we got from the track entry */ + if(!track_module->number || !track_module->type) + { status = VC_CONTAINER_ERROR_FORMAT_INVALID; goto error; } + + /* Check the encodings for the track are supported */ + if(track_module->encodings_num > MKV_MAX_ENCODINGS) + { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } + for(i = 0; i < track_module->encodings_num; i++) + { + if(track_module->encodings[i].type != MKV_CONTENT_ENCODING_COMPRESSION_HEADER || + !track_module->encodings[i].data_size) + { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } + } + + /* Find out the track type */ + if(track_module->type == 0x1) + es_type = VC_CONTAINER_ES_TYPE_VIDEO; + else if(track_module->type == 0x2) + es_type = VC_CONTAINER_ES_TYPE_AUDIO; + else if(track_module->type == 0x11) + es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; + + if(es_type == VC_CONTAINER_ES_TYPE_UNKNOWN) + { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; } + + if(!strcmp(track_module->codecid, "V_MS/VFW/FOURCC")) + { + if(vc_container_bitmapinfoheader_to_es_format(track->format->extradata, + track->format->extradata_size, &extra_offset, &extra_size, + track->format) == VC_CONTAINER_SUCCESS) + { + fourcc = track->format->codec; + is_bmih = 1; + } + track->format->extradata += extra_offset; + track->format->extradata_size = extra_size; + } + else if(!strcmp(track_module->codecid, "A_MS/ACM")) + { + if(vc_container_waveformatex_to_es_format(track->format->extradata, + track->format->extradata_size, &extra_offset, &extra_size, + track->format) == VC_CONTAINER_SUCCESS) + { + fourcc = track->format->codec; + is_wf = 1; + } + track->format->extradata += extra_offset; + track->format->extradata_size = extra_size; + } + else if((!strncmp(track_module->codecid, "A_AAC/MPEG2/", sizeof("A_AAC/MPEG2/")-1) || + !strncmp(track_module->codecid, "A_AAC/MPEG4/", sizeof("A_AAC/MPEG4/")-1)) && + !track->format->extradata_size) + { + static const unsigned int sample_rates[16] = + {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350}; + unsigned int samplerate, samplerate_idx, profile, sbr = 0; + uint8_t *extra; + + fourcc = mkv_codecid_to_fourcc(track_module->codecid, &variant); + + /* Create extra data */ + if( !strcmp( &track_module->codecid[12], "MAIN" ) ) profile = 0; + else if( !strcmp( &track_module->codecid[12], "LC" ) ) profile = 1; + else if( !strcmp( &track_module->codecid[12], "SSR" ) ) profile = 2; + else if( !strcmp( &track_module->codecid[12], "LC/SBR" ) ) { profile = 1; sbr = 1; } + else profile = 3; + + samplerate = track_module->es_type.audio.sampling_frequency; + for( samplerate_idx = 0; samplerate_idx < 13; samplerate_idx++ ) + if( sample_rates[samplerate_idx] == samplerate ) break; + + status = vc_container_track_allocate_extradata(p_ctx, track, sbr ? 5 : 2); + if(status != VC_CONTAINER_SUCCESS) goto error; + track->format->extradata_size = sbr ? 5 : 2; + extra = track->format->extradata; + + extra[0] = ((profile + 1) << 3) | ((samplerate_idx & 0xe) >> 1); + extra[1] = ((samplerate_idx & 0x1) << 7) | (track_module->es_type.audio.channels << 3); + + if(sbr) + { + unsigned int sync_extension_type = 0x2B7; + samplerate = track_module->es_type.audio.output_sampling_frequency; + for(samplerate_idx = 0; samplerate_idx < 13; samplerate_idx++) + if(sample_rates[samplerate_idx] == samplerate) break; + extra[2] = (sync_extension_type >> 3) & 0xFF; + extra[3] = ((sync_extension_type & 0x7) << 5) | 5; + extra[4] = (1 << 7) | (samplerate_idx << 3); + } + } + else fourcc = mkv_codecid_to_fourcc(track_module->codecid, &variant); + + if(!fourcc) + { + LOG_DEBUG(p_ctx, "codec id %s is not supported", track_module->codecid); + } + + LOG_DEBUG(p_ctx, "found track %4.4s", (char *)&fourcc); + track->format->codec = fourcc; + track->format->codec_variant = variant; + track->format->es_type = es_type; + + switch(es_type) + { + case VC_CONTAINER_ES_TYPE_VIDEO: + if(!is_bmih) + { + track->format->type->video.width = track_module->es_type.video.pixel_width; + track->format->type->video.height = track_module->es_type.video.pixel_height; + } + track->format->type->video.visible_width = track->format->type->video.width; + track->format->type->video.visible_height = track->format->type->video.height; + if(track_module->es_type.video.pixel_crop_left < track->format->type->video.visible_width && + track_module->es_type.video.pixel_crop_top < track->format->type->video.visible_height) + { + track->format->type->video.x_offset = track_module->es_type.video.pixel_crop_left; + track->format->type->video.y_offset = track_module->es_type.video.pixel_crop_right; + track->format->type->video.visible_width -= track->format->type->video.x_offset; + track->format->type->video.visible_height -= track->format->type->video.y_offset; + } + if(track_module->es_type.video.pixel_crop_right < track->format->type->video.visible_width && + track_module->es_type.video.pixel_crop_bottom < track->format->type->video.visible_height) + { + track->format->type->video.visible_width -= track_module->es_type.video.pixel_crop_right; + track->format->type->video.visible_height -= track_module->es_type.video.pixel_crop_bottom; + } + if(track_module->es_type.video.frame_rate) + { + track->format->type->video.frame_rate_den = 100; + track->format->type->video.frame_rate_num = 100 * track_module->es_type.video.frame_rate; + } + if(track_module->es_type.video.display_width && track_module->es_type.video.display_height) + { + track->format->type->video.par_num = track_module->es_type.video.display_width * + track->format->type->video.visible_height; + track->format->type->video.par_den = track_module->es_type.video.display_height * + track->format->type->video.visible_width; + vc_container_maths_rational_simplify(&track->format->type->video.par_num, + &track->format->type->video.par_den); + } + break; + case VC_CONTAINER_ES_TYPE_AUDIO: + if(is_wf) break; + track->format->type->audio.sample_rate = track_module->es_type.audio.sampling_frequency; + if(track_module->es_type.audio.output_sampling_frequency) + track->format->type->audio.sample_rate = track_module->es_type.audio.output_sampling_frequency; + track->format->type->audio.channels = track_module->es_type.audio.channels; + track->format->type->audio.bits_per_sample = track_module->es_type.audio.bit_depth; + break; + default: + case VC_CONTAINER_ES_TYPE_SUBPICTURE: + track->format->type->subpicture.encoding = VC_CONTAINER_CHAR_ENCODING_UTF8; + if(!strcmp(track_module->codecid, "S_TEXT/ASCII")) + track->format->type->subpicture.encoding = VC_CONTAINER_CHAR_ENCODING_UNKNOWN; + } + + track->is_enabled = true; + + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + error: + for(i = 0; i < MKV_MAX_ENCODINGS; i++) free(track_module->encodings[i].data); + vc_container_free_track(p_ctx, track); + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = module->parsing; + VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module; + uint64_t value; + + /* Deal with string elements */ + if( id == MKV_ELEMENT_ID_NAME || + id == MKV_ELEMENT_ID_LANGUAGE || + id == MKV_ELEMENT_ID_TRACK_CODEC_ID || + id == MKV_ELEMENT_ID_TRACK_CODEC_NAME ) + { + char stringbuf[MKV_MAX_STRING_SIZE+1]; + + if(size > MKV_MAX_STRING_SIZE) + { + LOG_DEBUG(p_ctx, "string truncated (%i>%i)", (int)size, MKV_MAX_STRING_SIZE); + size = MKV_MAX_STRING_SIZE; + } + if(READ_BYTES(p_ctx, stringbuf, size) != (size_t)size) + { + //XXX do sane thing + return STREAM_STATUS(p_ctx); + } + stringbuf[size] = 0; /* null terminate */ + + LOG_FORMAT(p_ctx, "%s", stringbuf); + + if(id == MKV_ELEMENT_ID_TRACK_CODEC_ID) + strncpy(track_module->codecid, stringbuf, MKV_CODECID_MAX-1); + + return VC_CONTAINER_SUCCESS; + } + + /* Deal with codec private data */ + if( id == MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE ) + { + status = vc_container_track_allocate_extradata(p_ctx, track, (unsigned int)size); + if(status != VC_CONTAINER_SUCCESS) return status; + track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); + return STREAM_STATUS(p_ctx); + } + + /* The rest are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + case MKV_ELEMENT_ID_TRACK_NUMBER: + track_module->number = value; break; + case MKV_ELEMENT_ID_TRACK_TYPE: + track_module->type = value; break; + case MKV_ELEMENT_ID_DEFAULT_DURATION: + track_module->frame_duration = value; break; + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_video( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; + uint64_t value; + + /* Deal with floating point values first */ + if(id == MKV_ELEMENT_ID_FRAME_RATE) + { + double fvalue; + status = mkv_read_element_data_float(p_ctx, size, &fvalue); + if(status != VC_CONTAINER_SUCCESS) return status; + track_module->es_type.video.frame_rate = fvalue; + return status; + } + + /* The rest are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + case MKV_ELEMENT_ID_PIXEL_WIDTH: track_module->es_type.video.pixel_width = value; break; + case MKV_ELEMENT_ID_PIXEL_HEIGHT: track_module->es_type.video.pixel_height = value; break; + case MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM: track_module->es_type.video.pixel_crop_bottom = value; break; + case MKV_ELEMENT_ID_PIXEL_CROP_TOP: track_module->es_type.video.pixel_crop_top = value; break; + case MKV_ELEMENT_ID_PIXEL_CROP_LEFT: track_module->es_type.video.pixel_crop_left = value; break; + case MKV_ELEMENT_ID_PIXEL_CROP_RIGHT: track_module->es_type.video.pixel_crop_right = value; break; + case MKV_ELEMENT_ID_DISPLAY_WIDTH: track_module->es_type.video.display_width = value; break; + case MKV_ELEMENT_ID_DISPLAY_HEIGHT: track_module->es_type.video.display_height = value; break; + case MKV_ELEMENT_ID_DISPLAY_UNIT: track_module->es_type.video.display_unit = value; break; + case MKV_ELEMENT_ID_ASPECT_RATIO_TYPE: track_module->es_type.video.aspect_ratio_type = value; break; + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_audio( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; + uint64_t value; + + /* Deal with floating point values first */ + if(id == MKV_ELEMENT_ID_SAMPLING_FREQUENCY || + id == MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY) + { + double fvalue; + status = mkv_read_element_data_float(p_ctx, size, &fvalue); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(id == MKV_ELEMENT_ID_SAMPLING_FREQUENCY) + track_module->es_type.audio.sampling_frequency = fvalue; + + if(id == MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY) + track_module->es_type.audio.output_sampling_frequency = fvalue; + + return status; + } + + /* The rest are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + case MKV_ELEMENT_ID_CHANNELS: track_module->es_type.audio.channels = value; break; + case MKV_ELEMENT_ID_BIT_DEPTH: track_module->es_type.audio.bit_depth = value; break; + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_element_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; + VC_CONTAINER_STATUS_T status; + status = mkv_read_elements(p_ctx, id, size); + track_module->encodings_num++; + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; + VC_CONTAINER_STATUS_T status; + uint64_t value; + + /* These are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(track_module->encodings_num >= MKV_MAX_ENCODINGS) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + switch(id) + { + case MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE: + if(value == 0) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_ZLIB; + if(value == 1) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_ENCRYPTION; + else track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_UNKNOWN; + break; + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_compression( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module; + VC_CONTAINER_STATUS_T status; + uint64_t value; + uint8_t *data; + + if(id == MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO) + { + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(value == 0) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_ZLIB; + if(value == 3) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_HEADER; + else track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_UNKNOWN; + return status; + } + + if(id == MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS) + { + if(track_module->encodings[track_module->encodings_num].type == MKV_CONTENT_ENCODING_COMPRESSION_HEADER) + { + if(size > MKV_MAX_ENCODING_DATA) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + data = malloc((int)size); + if(!data) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + track_module->encodings[track_module->encodings_num].data = data; + track_module->encodings[track_module->encodings_num].data_size = READ_BYTES(p_ctx, data, size); + if(track_module->encodings[track_module->encodings_num].data_size != size) + track_module->encodings[track_module->encodings_num].data_size = 0; + return STREAM_STATUS(p_ctx); + } + else + { + SKIP_BYTES(p_ctx, size); + } + return STREAM_STATUS(p_ctx); + } + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_info( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint64_t value; + double fvalue; + + switch(id) + { + case MKV_ELEMENT_ID_TIMECODE_SCALE: + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) break; + module->timecode_scale = value; + break; + case MKV_ELEMENT_ID_DURATION: + status = mkv_read_element_data_float(p_ctx, size, &fvalue); + if(status != VC_CONTAINER_SUCCESS) break; + module->duration = fvalue; + break; + case MKV_ELEMENT_ID_TITLE: + case MKV_ELEMENT_ID_MUXING_APP: + case MKV_ELEMENT_ID_WRITING_APP: + SKIP_STRING(p_ctx, size, "string"); + break; + + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_seek_head( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint64_t value; + + if(id == MKV_ELEMENT_ID_SEEK) + { + module->seekhead_elem_id = MKV_ELEMENT_ID_UNKNOWN; + module->seekhead_elem_offset = -1; + status = mkv_read_elements(p_ctx, id, size); + if(status == VC_CONTAINER_SUCCESS && !module->cues_offset && + module->seekhead_elem_id == MKV_ELEMENT_ID_CUES && module->seekhead_elem_offset) + module->cues_offset = module->seekhead_elem_offset; + if(status == VC_CONTAINER_SUCCESS && !module->tags_offset && + module->seekhead_elem_id == MKV_ELEMENT_ID_TAGS && module->seekhead_elem_offset) + module->tags_offset = module->seekhead_elem_offset; + return status; + } + + if(id == MKV_ELEMENT_ID_SEEK_ID) + { + MKV_ELEMENT_T *element = mkv_elements_list; + id = MKV_READ_ID(p_ctx, "Element ID"); + module->seekhead_elem_id = id; + + /* Find out which Element we are dealing with */ + while(element->id && id != element->id) element++; + LOG_FORMAT(p_ctx, "element: %s (ID 0x%x)", element->psz_name, id); + } + else if(id == MKV_ELEMENT_ID_SEEK_POSITION) + { + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + LOG_FORMAT(p_ctx, "offset: %"PRIi64, value + module->segment_offset); + module->seekhead_elem_offset = value + module->segment_offset; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_element_cues( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(id); + VC_CONTAINER_PARAM_UNUSED(size); + module->cues_offset = module->element_offset; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_cue_point( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint64_t value; + + /* All the values are unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + case MKV_ELEMENT_ID_CUE_TIME: + module->cue_timecode = value; break; + case MKV_ELEMENT_ID_CUE_TRACK: + module->cue_track = value; break; + case MKV_ELEMENT_ID_CUE_CLUSTER_POSITION: + module->cue_cluster_offset = value; break; + case MKV_ELEMENT_ID_CUE_BLOCK_NUMBER: + module->cue_block = value; break; + default: break; + } + + return status; +} + +static VC_CONTAINER_STATUS_T mkv_read_subelements_cluster( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint64_t value; + + /* The rest are just unsigned integers */ + status = mkv_read_element_data_uint(p_ctx, size, &value); + if(status != VC_CONTAINER_SUCCESS) return status; + + switch(id) + { + //XXX + case MKV_ELEMENT_ID_TIMECODE: module->state.cluster_timecode = value; break; + case MKV_ELEMENT_ID_BLOCK_DURATION: module->state.frame_duration = value; break; + default: break; + } + + return status; +} + +/*******************************/ + +static VC_CONTAINER_STATUS_T mkv_skip_element(VC_CONTAINER_T *p_ctx, + MKV_READER_STATE_T *state) +{ + /* Skip any trailing data from the current element */ + int64_t skip = state->levels[state->level].offset + + state->levels[state->level].size - STREAM_POSITION(p_ctx); + + if(skip < 0) /* Check for overruns */ + { + /* Things have gone really bad here and we ended up reading past the end of the + * element. We could maybe try to be clever and recover by seeking back to the end + * of the element. However if we get there, the file is clearly corrupted so there's + * no guarantee it would work anyway. */ + LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of the element", -skip); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if(skip) + LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread at the end of element", skip); + + state->level--; + + if (skip >= MKV_MAX_ELEMENT_SIZE) + return SEEK(p_ctx, STREAM_POSITION(p_ctx) + skip); + SKIP_BYTES(p_ctx, skip); + return STREAM_STATUS(p_ctx); +} + +static VC_CONTAINER_STATUS_T mkv_find_next_element(VC_CONTAINER_T *p_ctx, + MKV_READER_STATE_T *state, MKV_ELEMENT_ID_T element_id) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t element_size, element_offset; + MKV_ELEMENT_ID_T id; + + /* Skip all elements until we find the next requested element */ + do + { + MKV_ELEMENT_T *element = mkv_cluster_elements_list; + + /* Check whether we've reach the end of the parent element */ + if(STREAM_POSITION(p_ctx) >= state->levels[state->level].offset + + state->levels[state->level].size) + return VC_CONTAINER_ERROR_NOT_FOUND; + + status = mkv_read_element_header(p_ctx, INT64_C(1) << 30, &id, + &element_size, state->levels[state->level].id, &element); + element_offset = STREAM_POSITION(p_ctx); + if(status != VC_CONTAINER_SUCCESS) return status; + if(id == element_id) break; + if(element_id == MKV_ELEMENT_ID_BLOCKGROUP && id == MKV_ELEMENT_ID_SIMPLE_BLOCK) break; + + if(element_id == MKV_ELEMENT_ID_BLOCK && id == MKV_ELEMENT_ID_REFERENCE_BLOCK) + state->seen_ref_block = 1; + + /* Check whether we've reached the end of the parent element */ + if(STREAM_POSITION(p_ctx) + element_size >= state->levels[state->level].offset + + state->levels[state->level].size) + return VC_CONTAINER_ERROR_NOT_FOUND; + + status = mkv_read_element_data(p_ctx, element, element_size, INT64_C(1) << 30); +#if 0 + if(element_size < MKV_MAX_ELEMENT_SIZE) SKIP_BYTES(p_ctx, element_size); + else SEEK(p_ctx, STREAM_POSITION(p_ctx) + element_size); +#endif + } while (status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + return STREAM_STATUS(p_ctx); + + state->level++; + state->levels[state->level].offset = element_offset; + state->levels[state->level].size = element_size; + state->levels[state->level].id = id; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T mkv_find_next_segment(VC_CONTAINER_T *p_ctx, + MKV_READER_STATE_T *state) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t element_size, element_offset; + MKV_ELEMENT_ID_T id; + + /* Skip all elements until we find the next segment */ + do + { + MKV_ELEMENT_T *element = mkv_cluster_elements_list; + + status = mkv_read_element_header(p_ctx, INT64_C(-1), &id, + &element_size, MKV_ELEMENT_ID_INVALID, &element); + + element_offset = STREAM_POSITION(p_ctx); + if(status != VC_CONTAINER_SUCCESS) return status; + if(id == MKV_ELEMENT_ID_SEGMENT) break; + + status = mkv_read_element_data(p_ctx, element, element_size, INT64_C(-1)); + } while (status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + return STREAM_STATUS(p_ctx); + + state->level++; + state->levels[state->level].offset = element_offset; + state->levels[state->level].size = element_size; + state->levels[state->level].id = id; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T mkv_find_next_block(VC_CONTAINER_T *p_ctx, MKV_READER_STATE_T *state) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; + + do + { + if(state->level < 0) + { +#ifdef ENABLE_MKV_EXTRA_LOGGING + LOG_DEBUG(p_ctx, "find segment"); +#endif + status = mkv_find_next_segment(p_ctx, state); + if(status == VC_CONTAINER_SUCCESS) + { + LOG_ERROR(p_ctx, "multi-segment files not supported"); + status = VC_CONTAINER_ERROR_EOS; + break; + } + } + if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK || + state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) + { + status = mkv_skip_element(p_ctx, state); + } + if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCKGROUP) + { +#ifdef ENABLE_MKV_EXTRA_LOGGING + LOG_DEBUG(p_ctx, "find block"); +#endif + state->seen_ref_block = 0; + state->frame_duration = 0; + status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_BLOCK); + if(status == VC_CONTAINER_SUCCESS) break; + + if(status == VC_CONTAINER_ERROR_NOT_FOUND) + status = mkv_skip_element(p_ctx, state); + } + if(state->levels[state->level].id == MKV_ELEMENT_ID_CLUSTER) + { +#ifdef ENABLE_MKV_EXTRA_LOGGING + LOG_DEBUG(p_ctx, "find blockgroup or simpleblock"); +#endif + state->frame_duration = 0; + status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_BLOCKGROUP); + if(status == VC_CONTAINER_SUCCESS && + state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) break; + if(status == VC_CONTAINER_ERROR_NOT_FOUND) + status = mkv_skip_element(p_ctx, state); + } + if(state->levels[state->level].id == MKV_ELEMENT_ID_SEGMENT) + { +#ifdef ENABLE_MKV_EXTRA_LOGGING + LOG_DEBUG(p_ctx, "find cluster"); +#endif + status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_CLUSTER); + if(status == VC_CONTAINER_ERROR_NOT_FOUND) + status = mkv_skip_element(p_ctx, state); + } + + } while(status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); + + return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status; +} + +static VC_CONTAINER_STATUS_T mkv_read_next_frame_header(VC_CONTAINER_T *p_ctx, + MKV_READER_STATE_T *state, uint32_t *pi_track, uint32_t *pi_length) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = 0; + unsigned int i, track, flags, is_first_lace = 0; + int64_t size, pts; + + if ((state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK || + state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) && + state->levels[state->level].data_start + state->levels[state->level].data_offset < + state->levels[state->level].size) + goto end; + + status = mkv_find_next_block(p_ctx, state); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* We have a block */ + size = state->levels[state->level].size; + track = MKV_READ_UINT(p_ctx, "Track Number"); LOG_FORMAT(p_ctx, "Track Number: %u", track); + pts = (int16_t)MKV_READ_U16(p_ctx, "Timecode"); + flags = MKV_READ_U8(p_ctx, "Flags"); + if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK) flags &= 0x0F; + + //TODO improve sanity checking + /* Sanity checking */ + if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; + if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); + + for (i = 0; i < p_ctx->tracks_num; i++) + if (p_ctx->tracks[i]->priv->module->number == track) + { track_module = p_ctx->tracks[i]->priv->module; break; } + + /* Finding out if we have a keyframe when dealing with an MKV_ELEMENT_ID_BLOCK is tricky */ + if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK && + i < p_ctx->tracks_num && p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + /* The absence of a ReferenceBlock element tells us if we are a keyframe or not. + * The problem we have is that this element can appear before as well as after the block element. + * To avoid seeking to look for this element, we do some guess work. */ + if(!state->seen_ref_block && state->level && + state->levels[state->level].offset + state->levels[state->level].size >= + state->levels[state->level-1].offset + state->levels[state->level-1].size) + flags |= 0x80; + } + + /* Take care of the lacing */ + state->lacing_num_frames = 0; + if(i < p_ctx->tracks_num && (flags & 0x06)) + { + unsigned int i, value = 0; + int32_t fs = 0; + + state->lacing_num_frames = MKV_READ_U8(p_ctx, "Lacing Head"); + state->lacing_size = 0; + switch((flags & 0x06)>>1) + { + case 1: /* Xiph lacing */ + for(i = 0; i < state->lacing_num_frames; i++, fs = 0) + { + do { + value = vc_container_io_read_uint8(p_ctx->priv->io); + fs += value; size--; + } while(value == 255); + LOG_FORMAT(p_ctx, "Frame Size: %i", (int)fs); + if(state->lacing_num_frames > MKV_MAX_LACING_NUM) continue; + state->lacing_sizes[state->lacing_num_frames-(i+1)] = fs; + } + break; + case 3: /* EBML lacing */ + for(i = 0; i < state->lacing_num_frames; i++) + { + if(!i) fs = MKV_READ_UINT(p_ctx, "Frame Size"); + else fs += MKV_READ_SINT(p_ctx, "Frame Size"); + LOG_FORMAT(p_ctx, "Frame Size: %i", (int)fs); + if(state->lacing_num_frames > MKV_MAX_LACING_NUM) continue; + state->lacing_sizes[state->lacing_num_frames-(i+1)] = fs; + } + break; + default: /* Fixed-size lacing */ + state->lacing_size = size / (state->lacing_num_frames+1); + break; + } + + /* There is a max number of laced frames we can support but after we can still give back + * all the other frames in 1 packet */ + if(state->lacing_num_frames > MKV_MAX_LACING_NUM) + state->lacing_num_frames = MKV_MAX_LACING_NUM; + + /* Sanity check the size of the frames */ + if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; + if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); + state->lacing_current_size = state->lacing_size; + if(!state->lacing_size) + { + int64_t frames_size = 0; + for(i = state->lacing_num_frames; i > 0; i--) + { + if(frames_size + state->lacing_sizes[i-1] > size) /* return error ? */ + state->lacing_sizes[i-1] = size - frames_size; + frames_size += state->lacing_sizes[i-1]; + } + } + state->lacing_current_size = 0; + state->lacing_num_frames++; /* Will be decremented further down */ + is_first_lace = 1; + } + + state->track = i; + state->pts = (state->cluster_timecode + pts) * module->timecode_scale; + if(track_module) state->pts *= track_module->timecode_scale; + state->pts /= 1000; + state->flags = flags; + + state->frame_duration = state->frame_duration * module->timecode_scale / 1000; + if(state->lacing_num_frames) state->frame_duration /= state->lacing_num_frames; + if(!state->frame_duration && track_module) + state->frame_duration = track_module->frame_duration / 1000; + + state->levels[state->level].data_start = STREAM_POSITION(p_ctx) - + state->levels[state->level].offset; + state->levels[state->level].data_offset = 0; + + /* Deal with header stripping compression */ + state->header_size = 0; + if(track_module && track_module->encodings_num) + { + state->header_size = track_module->encodings[0].data_size; + state->header_data = track_module->encodings[0].data; + } + state->header_size_backup = state->header_size; + + end: + *pi_length = state->levels[state->level].size - state->levels[state->level].data_start - + state->levels[state->level].data_offset + state->header_size; + *pi_track = state->track; + + /* Special case for lacing */ + if(state->lacing_num_frames && + state->levels[state->level].data_offset >= state->lacing_current_size) + { + /* We need to switch to the next lace */ + if(--state->lacing_num_frames) + { + unsigned int lace_size = state->lacing_size; + if(!state->lacing_size) lace_size = state->lacing_sizes[state->lacing_num_frames-1]; + state->lacing_current_size = lace_size; + } + state->levels[state->level].data_start += state->levels[state->level].data_offset; + state->levels[state->level].data_offset = 0; + if(!is_first_lace && state->frame_duration) + state->pts += state->frame_duration; + else if(!is_first_lace) + state->pts = VC_CONTAINER_TIME_UNKNOWN; + + /* Deal with header stripping compression */ + state->header_data -= (state->header_size_backup - state->header_size); + state->header_size = state->header_size_backup; + } + if(state->lacing_num_frames) + *pi_length = state->lacing_current_size - state->levels[state->level].data_offset + state->header_size; + + return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status; +} + +static VC_CONTAINER_STATUS_T mkv_read_frame_data(VC_CONTAINER_T *p_ctx, + MKV_READER_STATE_T *state, uint8_t *p_data, uint32_t *pi_length) +{ + uint64_t size; + uint32_t header_size; + + size = state->levels[state->level].size - state->levels[state->level].data_start - + state->levels[state->level].data_offset; + + /* Special case for lacing */ + if(state->lacing_num_frames) + { + size = state->lacing_current_size - state->levels[state->level].data_offset; + + if(!p_data) + { + size = SKIP_BYTES(p_ctx, size); + state->levels[state->level].data_offset += size; + return STREAM_STATUS(p_ctx); + } + } + + size += state->header_size; + + if(!p_data) return mkv_skip_element(p_ctx, state); + if(size > *pi_length) size = *pi_length; + + header_size = state->header_size; + if(header_size) + { + if(header_size > size) header_size = size; + memcpy(p_data, state->header_data, header_size); + state->header_size -= header_size; + state->header_data += header_size; + size -= header_size; + } + + size = READ_BYTES(p_ctx, p_data + header_size, size); + state->levels[state->level].data_offset += size; + *pi_length = size + header_size; + + return STREAM_STATUS(p_ctx); +} + +/***************************************************************************** + Functions exported as part of the Container Module API + *****************************************************************************/ + +static VC_CONTAINER_STATUS_T mkv_reader_read(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *p_track = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t buffer_size = 0, track = 0, data_size; + MKV_READER_STATE_T *state = &module->state; + + /* If a specific track has been selected, we need to use the track packet state */ + if(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + { + p_track = p_ctx->tracks[p_packet->track]; + state = p_track->priv->module->state; + } + + /**/ + if(state->eos) return VC_CONTAINER_ERROR_EOS; + if(state->corrupted) return VC_CONTAINER_ERROR_CORRUPTED; + + /* Look at the next frame header */ + status = mkv_read_next_frame_header(p_ctx, state, &track, &data_size); + if(status == VC_CONTAINER_ERROR_EOS) state->eos = true; + if(status == VC_CONTAINER_ERROR_CORRUPTED) state->corrupted = true; + if(status != VC_CONTAINER_SUCCESS) return status; + + if(track >= p_ctx->tracks_num || !p_ctx->tracks[track]->is_enabled) + { + /* Skip frame */ + status = mkv_read_frame_data(p_ctx, state, 0, &data_size); + if (status != VC_CONTAINER_SUCCESS) return status; + return VC_CONTAINER_ERROR_CONTINUE; + } + + if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */ + return mkv_read_frame_data(p_ctx, state, 0, &data_size); + + p_packet->dts = p_packet->pts = state->pts; + p_packet->flags = 0; + if(state->flags & 0x80) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + if(!state->levels[state->level].data_offset) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + p_packet->size = data_size; + p_packet->track = track; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + return mkv_read_frame_data(p_ctx, state, 0, &data_size ); + else if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + /* Read the frame data */ + buffer_size = p_packet->buffer_size; + status = mkv_read_frame_data(p_ctx, state, p_packet->data, &buffer_size); + if(status != VC_CONTAINER_SUCCESS) + { + /* FIXME */ + return status; + } + + p_packet->size = buffer_size; + if(buffer_size != data_size) + p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mkv_reader_seek(VC_CONTAINER_T *p_ctx, + int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + MKV_READER_STATE_T *state = &module->state; + uint64_t offset = 0, prev_offset = 0, position = STREAM_POSITION(p_ctx); + int64_t time_offset = 0, prev_time_offset = 0; + unsigned int i, video_track; + MKV_ELEMENT_T *element = mkv_cue_elements_list; + int64_t size, element_size; + MKV_ELEMENT_ID_T id; + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + + /* Find out if we have a video track */ + for(video_track = 0; video_track < p_ctx->tracks_num; video_track++) + if(p_ctx->tracks[video_track]->is_enabled && + p_ctx->tracks[video_track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break; + + if(!*p_offset) goto end; /* Nothing much to do */ + if(!module->cues_offset) {status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; goto error;} + + /* We need to do a search in the cue list */ + status = SEEK(p_ctx, module->cues_offset); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* First read the header of the cues element */ + status = mkv_read_element_header(p_ctx, INT64_C(-1) /* TODO */, &id, &element_size, + MKV_ELEMENT_ID_SEGMENT, &element); + if(status != VC_CONTAINER_SUCCESS || id != MKV_ELEMENT_ID_CUES) goto error; + size = element_size; + + module->elements_list = mkv_cue_elements_list; + do + { + MKV_ELEMENT_T *element = mkv_cue_elements_list; + int64_t element_offset = STREAM_POSITION(p_ctx); + + /* Exit condition for when we've scanned the whole cues list */ + if(size <= 0) + { + if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + break; /* Just use the last valid entry in that case */ + status = VC_CONTAINER_ERROR_EOS; + goto error; + } + + status = mkv_read_element_header(p_ctx, size, &id, &element_size, + MKV_ELEMENT_ID_CUES, &element); + size -= STREAM_POSITION(p_ctx) - element_offset; + if(status == VC_CONTAINER_SUCCESS && element->id != MKV_ELEMENT_ID_UNKNOWN) + status = mkv_read_element_data(p_ctx, element, element_size, size); + + if(status != VC_CONTAINER_SUCCESS || element->id == MKV_ELEMENT_ID_UNKNOWN) + { + if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + break; /* Just use the last valid entry in that case */ + goto error; + } + + size -= element_size; + if(id != MKV_ELEMENT_ID_CUE_POINT) continue; + + /* Ignore cue points which don't belong to the track we want */ + if(video_track != p_ctx->tracks_num && + p_ctx->tracks[video_track]->priv->module->number != module->cue_track) continue; + + time_offset = module->cue_timecode * module->timecode_scale / 1000; + offset = module->cue_cluster_offset; + LOG_DEBUG(p_ctx, "INDEX: %"PRIi64, time_offset); + if( time_offset > *p_offset ) + { + if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) + { + time_offset = prev_time_offset; + offset = prev_offset; + } + break; + } + prev_time_offset = time_offset; + prev_offset = offset; + } while( 1 ); + module->elements_list = mkv_elements_list; + *p_offset = time_offset; + + end: + /* Try seeking to the requested position */ + status = SEEK(p_ctx, module->segment_offset + offset); + if(status != VC_CONTAINER_SUCCESS && status != VC_CONTAINER_ERROR_EOS) goto error; + + /* Reinitialise the state */ + memset(state, 0, sizeof(*state)); + state->levels[0].offset = module->segment_offset; + state->levels[0].size = module->segment_size; + state->levels[0].id = MKV_ELEMENT_ID_SEGMENT; + if(status == VC_CONTAINER_ERROR_EOS) state->eos = true; + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i]; + p_track->priv->module->state = state; + } + + /* If we have a video track, we skip frames until the next keyframe */ + for(i = 0; video_track != p_ctx->tracks_num && i < 200 /* limit search */; ) + { + uint32_t track, data_size; + status = mkv_read_next_frame_header(p_ctx, state, &track, &data_size); + if(status != VC_CONTAINER_SUCCESS) break; //FIXME + if(track == video_track) i++; + if(track == video_track && (state->flags & 0x80) && + state->pts >= time_offset) break; + + /* Skip frame */ + status = mkv_read_frame_data(p_ctx, state, 0, &data_size); + } + + return VC_CONTAINER_SUCCESS; + + error: + /* Reset everything as it was before the seek */ + SEEK(p_ctx, position); + if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FAILED; + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mkv_reader_close(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i, j; + + for(i = 0; i < p_ctx->tracks_num; i++) + { + for(j = 0; j < MKV_MAX_ENCODINGS; j++) + free(p_ctx->tracks[i]->priv->module->encodings[j].data); + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + } + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T mkv_reader_open(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + uint8_t buffer[4]; + + // Can start with ASCII strings ???? + + /* Check for an EBML element */ + if(PEEK_BYTES(p_ctx, buffer, 4) < 4 || + buffer[0] != 0x1A || buffer[1] != 0x45 || buffer[2] != 0xDF || buffer[3] != 0xA3) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* + * We are dealing with an MKV file + */ + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) {status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error;} + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + module->elements_list = mkv_elements_list; + + /* Read and sanity check the EBML header */ + status = mkv_read_element(p_ctx, INT64_C(-1), MKV_ELEMENT_ID_UNKNOWN); + if(status != VC_CONTAINER_SUCCESS) goto error; + if(!module->is_doctype_valid) {status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; goto error;} + + /* Read the other elements until we find the start of the data */ + do + { + status = mkv_read_element(p_ctx, INT64_C(-1), MKV_ELEMENT_ID_UNKNOWN); + if(status != VC_CONTAINER_SUCCESS) break; + + if(module->cluster_offset) break; + } while(1); + + /* Bail out if we didn't find a track */ + if(!p_ctx->tracks_num) + { + status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE; + goto error; + } + + /* + * We now have all the information we really need to start playing the stream + */ + + p_ctx->priv->pf_close = mkv_reader_close; + p_ctx->priv->pf_read = mkv_reader_read; + p_ctx->priv->pf_seek = mkv_reader_seek; + p_ctx->duration = module->duration / 1000 * module->timecode_scale; + + /* Check if we're done */ + if(!STREAM_SEEKABLE(p_ctx)) + return VC_CONTAINER_SUCCESS; + + if(module->cues_offset && (int64_t)module->cues_offset < p_ctx->size) + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + if(module->tags_offset) + { + status = SEEK(p_ctx, module->tags_offset); + if(status == VC_CONTAINER_SUCCESS) + status = mkv_read_element(p_ctx, INT64_C(-1) /*FIXME*/, MKV_ELEMENT_ID_SEGMENT); + } + + /* Seek back to the start of the data */ + return SEEK(p_ctx, module->state.levels[1].offset); + + error: + LOG_DEBUG(p_ctx, "mkv: error opening stream (%i)", status); + if(module) mkv_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open mkv_reader_open +#endif diff --git a/containers/mp4/CMakeLists.txt b/containers/mp4/CMakeLists.txt new file mode 100755 index 0000000..87892c2 --- /dev/null +++ b/containers/mp4/CMakeLists.txt @@ -0,0 +1,19 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_mp4 ${LIBRARY_TYPE} mp4_reader.c) + +target_link_libraries(reader_mp4 containers) + +install(TARGETS reader_mp4 DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_mp4 ${LIBRARY_TYPE} mp4_writer.c) + +target_link_libraries(writer_mp4 containers) + +install(TARGETS writer_mp4 DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/mp4/mp4_common.h b/containers/mp4/mp4_common.h new file mode 100755 index 0000000..8718535 --- /dev/null +++ b/containers/mp4/mp4_common.h @@ -0,0 +1,128 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MP4_COMMON_H +#define MP4_COMMON_H + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef enum { + MP4_BOX_TYPE_UNKNOWN = 0, + MP4_BOX_TYPE_ROOT = VC_FOURCC('r','o','o','t'), + MP4_BOX_TYPE_FTYP = VC_FOURCC('f','t','y','p'), + MP4_BOX_TYPE_MDAT = VC_FOURCC('m','d','a','t'), + MP4_BOX_TYPE_MOOV = VC_FOURCC('m','o','o','v'), + MP4_BOX_TYPE_MVHD = VC_FOURCC('m','v','h','d'), + MP4_BOX_TYPE_TRAK = VC_FOURCC('t','r','a','k'), + MP4_BOX_TYPE_TKHD = VC_FOURCC('t','k','h','d'), + MP4_BOX_TYPE_MDIA = VC_FOURCC('m','d','i','a'), + MP4_BOX_TYPE_MDHD = VC_FOURCC('m','d','h','d'), + MP4_BOX_TYPE_HDLR = VC_FOURCC('h','d','l','r'), + MP4_BOX_TYPE_MINF = VC_FOURCC('m','i','n','f'), + MP4_BOX_TYPE_VMHD = VC_FOURCC('v','m','h','d'), + MP4_BOX_TYPE_SMHD = VC_FOURCC('s','m','h','d'), + MP4_BOX_TYPE_DINF = VC_FOURCC('d','i','n','f'), + MP4_BOX_TYPE_DREF = VC_FOURCC('d','r','e','f'), + MP4_BOX_TYPE_STBL = VC_FOURCC('s','t','b','l'), + MP4_BOX_TYPE_STSD = VC_FOURCC('s','t','s','d'), + MP4_BOX_TYPE_STTS = VC_FOURCC('s','t','t','s'), + MP4_BOX_TYPE_CTTS = VC_FOURCC('c','t','t','s'), + MP4_BOX_TYPE_STSC = VC_FOURCC('s','t','s','c'), + MP4_BOX_TYPE_STSZ = VC_FOURCC('s','t','s','z'), + MP4_BOX_TYPE_STCO = VC_FOURCC('s','t','c','o'), + MP4_BOX_TYPE_CO64 = VC_FOURCC('c','o','6','4'), + MP4_BOX_TYPE_STSS = VC_FOURCC('s','t','s','s'), + MP4_BOX_TYPE_VIDE = VC_FOURCC('v','i','d','e'), + MP4_BOX_TYPE_SOUN = VC_FOURCC('s','o','u','n'), + MP4_BOX_TYPE_TEXT = VC_FOURCC('t','e','x','t'), + MP4_BOX_TYPE_FREE = VC_FOURCC('f','r','e','e'), + MP4_BOX_TYPE_SKIP = VC_FOURCC('s','k','i','p'), + MP4_BOX_TYPE_WIDE = VC_FOURCC('w','i','d','e'), + MP4_BOX_TYPE_PNOT = VC_FOURCC('p','m','o','t'), + MP4_BOX_TYPE_PICT = VC_FOURCC('P','I','C','T'), + MP4_BOX_TYPE_UDTA = VC_FOURCC('u','d','t','a'), + MP4_BOX_TYPE_UUID = VC_FOURCC('u','u','i','d'), + MP4_BOX_TYPE_ESDS = VC_FOURCC('e','s','d','s'), + MP4_BOX_TYPE_AVCC = VC_FOURCC('a','v','c','C'), + MP4_BOX_TYPE_D263 = VC_FOURCC('d','2','6','3'), + MP4_BOX_TYPE_DAMR = VC_FOURCC('d','a','m','r'), + MP4_BOX_TYPE_DAWP = VC_FOURCC('d','a','w','p'), + MP4_BOX_TYPE_DEVC = VC_FOURCC('d','e','v','c'), + MP4_BOX_TYPE_WAVE = VC_FOURCC('w','a','v','e'), + MP4_BOX_TYPE_ZERO = 0 +} MP4_BOX_TYPE_T; + +typedef enum { + MP4_BRAND_ISOM = VC_FOURCC('i','s','o','m'), + MP4_BRAND_MP42 = VC_FOURCC('m','p','4','2'), + MP4_BRAND_3GP4 = VC_FOURCC('3','g','p','4'), + MP4_BRAND_3GP5 = VC_FOURCC('3','g','p','5'), + MP4_BRAND_3GP6 = VC_FOURCC('3','g','p','6'), + MP4_BRAND_SKM2 = VC_FOURCC('s','k','m','2'), + MP4_BRAND_SKM3 = VC_FOURCC('s','k','m','3'), + MP4_BRAND_QT = VC_FOURCC('q','t',' ',' '), + MP4_BRAND_NUM +} MP4_BRAND_T; + +typedef enum +{ + MP4_SAMPLE_TABLE_STTS = 0, /* decoding time to sample */ + MP4_SAMPLE_TABLE_STSZ = 1, /* sample size */ + MP4_SAMPLE_TABLE_STSC = 2, /* sample to chunk */ + MP4_SAMPLE_TABLE_STCO = 3, /* sample to chunk-offset */ + MP4_SAMPLE_TABLE_STSS = 4, /* sync sample */ + MP4_SAMPLE_TABLE_CO64 = 5, /* sample to chunk-offset */ + MP4_SAMPLE_TABLE_CTTS = 6, /* composite time to sample */ + MP4_SAMPLE_TABLE_NUM +} MP4_SAMPLE_TABLE_T; + +/* Values for object_type_indication (mp4_decoder_config_descriptor) + * see ISO/IEC 14496-1:2001(E) section 8.6.6.2 table 8 p. 30 + * see ISO/IEC 14496-15:2003 (draft) section 4.2.2 table 3 p. 11 + * see SKT Spec 8.2.3 p. 107 + * see 3GPP2 Spec v1.0 p. 22 */ +#define MP4_MPEG4_VISUAL_OBJECT_TYPE 0x20 /* visual ISO/IEC 14496-2 */ +#define MP4_MPEG4_H264_OBJECT_TYPE 0x21 /* visual ISO/IEC 14496-10 */ +#define MP4_MPEG4_H264_PS_OBJECT_TYPE 0x22 /* visual ISO/IEC 14496-10 (used for parameter ES) */ +#define MP4_MPEG4_AAC_LC_OBJECT_TYPE 0x40 /* audio ISO/IEC 14496-3 */ +#define MP4_MPEG2_SP_OBJECT_TYPE 0x60 /* visual ISO/IEC 13818-2 Simple Profile */ +#define MP4_MPEG2_MP_OBJECT_TYPE 0x61 /* visual ISO/IEC 13818-2 Main Profile */ +#define MP4_MPEG2_SNR_OBJECT_TYPE 0x62 /* visual ISO/IEC 13818-2 SNR Profile */ +#define MP4_MPEG2_AAC_LC_OBJECT_TYPE 0x67 /* audio ISO/IEC 13818-7 LowComplexity Profile */ +#define MP4_MP3_OBJECT_TYPE 0x69 /* audio ISO/IEC 13818-3 */ +#define MP4_MPEG1_VISUAL_OBJECT_TYPE 0x6A /* visual ISO/IEC 11172-2 */ +#define MP4_MPEG1_AUDIO_OBJECT_TYPE 0x6B /* audio ISO/IEC 11172-3 */ +#define MP4_JPEG_OBJECT_TYPE 0x6C /* visual ISO/IEC 10918-1 */ +#define MP4_SKT_EVRC_2V1_OBJECT_TYPE 0x82 /* SKT spec V2.1 for EVRC */ +#define MP4_KTF_EVRC_OBJECT_TYPE 0xC2 /* KTF spec V1.2 for EVRC */ +#define MP4_KTF_AMR_OBJECT_TYPE 0xC4 /* KTF spec V1.2 for AMR */ +#define MP4_KTF_MP3_OBJECT_TYPE 0xC5 /* KTF spec V1.2 for MP3 */ +#define MP4_SKT_TEXT_OBJECT_TYPE 0xD0 /* SKT spec V2.2 for Text */ +#define MP4_SKT_EVRC_OBJECT_TYPE 0xD1 /* SKT spec V2.2 for EVRC */ +#define MP4_3GPP2_QCELP_OBJECT_TYPE 0xE1 /* 3GPP2 spec V1.0 for QCELP13K */ + +#endif /* MP4_COMMON_H */ diff --git a/containers/mp4/mp4_reader.c b/containers/mp4/mp4_reader.c new file mode 100755 index 0000000..08341f5 --- /dev/null +++ b/containers/mp4/mp4_reader.c @@ -0,0 +1,1879 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +/* Work-around for MSVC debugger issue */ +#define VC_CONTAINER_MODULE_T VC_CONTAINER_MODULE_MP4_READER_T +#define VC_CONTAINER_TRACK_MODULE_T VC_CONTAINER_TRACK_MODULE_MP4_READER_T + +#define CONTAINER_IS_BIG_ENDIAN +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#include "containers/mp4/mp4_common.h" +#undef CONTAINER_HELPER_LOG_INDENT +#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->box_level + +VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T *p_ctx ); + +/****************************************************************************** +TODO: +- aspect ratio +- itunes gapless +- edit list +- subpicture track +******************************************************************************/ + +/****************************************************************************** +Defines. +******************************************************************************/ +#define MP4_TRACKS_MAX 16 + +#define MP4_BOX_MIN_HEADER_SIZE 8 +#define MP4_MAX_BOX_SIZE (1<<29) /* Does not apply to the mdat box */ +#define MP4_MAX_BOX_LEVEL 10 + +#define MP4_MAX_SAMPLES_BATCH_SIZE (16*1024) + +#define MP4_SKIP_U8(ctx,n) (size -= 1, SKIP_U8(ctx,n)) +#define MP4_SKIP_U16(ctx,n) (size -= 2, SKIP_U16(ctx,n)) +#define MP4_SKIP_U24(ctx,n) (size -= 3, SKIP_U24(ctx,n)) +#define MP4_SKIP_U32(ctx,n) (size -= 4, SKIP_U32(ctx,n)) +#define MP4_SKIP_U64(ctx,n) (size -= 8, SKIP_U64(ctx,n)) +#define MP4_READ_U8(ctx,n) (size -= 1, READ_U8(ctx,n)) +#define MP4_READ_U16(ctx,n) (size -= 2, READ_U16(ctx,n)) +#define MP4_READ_U24(ctx,n) (size -= 3, READ_U24(ctx,n)) +#define MP4_READ_U32(ctx,n) (size -= 4, READ_U32(ctx,n)) +#define MP4_READ_U64(ctx,n) (size -= 8, READ_U64(ctx,n)) +#define MP4_READ_FOURCC(ctx,n) (size -= 4, READ_FOURCC(ctx,n)) +#define MP4_SKIP_FOURCC(ctx,n) (size -= 4, SKIP_FOURCC(ctx,n)) +#define MP4_READ_BYTES(ctx,buffer,sz) (size -= sz, READ_BYTES(ctx,buffer,sz)) +#define MP4_SKIP_BYTES(ctx,sz) (size -= sz, SKIP_BYTES(ctx,sz)) +#define MP4_SKIP_STRING(ctx,sz,n) (size -= sz, SKIP_STRING(ctx,sz,n)) + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef struct +{ + VC_CONTAINER_STATUS_T status; + + int64_t duration; + int64_t pts; + int64_t dts; + + uint32_t sample; + int64_t offset; + unsigned int sample_offset; + unsigned int sample_size; + + uint32_t sample_duration; + uint32_t sample_duration_count; + int32_t sample_composition_offset; + uint32_t sample_composition_count; + + uint32_t next_sync_sample; + bool keyframe; + + uint32_t samples_per_chunk; + uint32_t chunks; + uint32_t samples_in_chunk; + + struct { + uint32_t entry; + } sample_table[MP4_SAMPLE_TABLE_NUM]; + +} MP4_READER_STATE_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + MP4_READER_STATE_T state; + + int64_t timescale; + uint8_t object_type_indication; + + uint32_t sample_size; + struct { + int64_t offset; + uint32_t entries; + uint32_t entry_size; + } sample_table[MP4_SAMPLE_TABLE_NUM]; + + uint32_t samples_batch_size; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + int64_t box_offset; + int box_level; + + MP4_BRAND_T brand; + + int64_t timescale; + + VC_CONTAINER_TRACK_T *tracks[MP4_TRACKS_MAX]; + unsigned int current_track; + + bool found_moov; + int64_t data_offset; + int64_t data_size; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Static functions within this file. +******************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box( VC_CONTAINER_T *p_ctx, int64_t size, MP4_BOX_TYPE_T parent_type ); +static VC_CONTAINER_STATUS_T mp4_read_boxes( VC_CONTAINER_T *p_ctx, int64_t size, MP4_BOX_TYPE_T type ); +static VC_CONTAINER_STATUS_T mp4_read_box_ftyp( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_moov( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_mvhd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_trak( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_tkhd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_mdia( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_mdhd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_hdlr( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_minf( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_vmhd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_smhd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_dinf( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_dref( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stbl( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stsd( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stts( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_ctts( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stsc( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stsz( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stco( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_co64( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_stss( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_vide( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_soun( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_text( VC_CONTAINER_T *p_ctx, int64_t size ); + +static VC_CONTAINER_STATUS_T mp4_read_box_esds( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_vide_avcC( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_vide_d263( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_soun_damr( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_soun_dawp( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_soun_devc( VC_CONTAINER_T *p_ctx, int64_t size ); +static VC_CONTAINER_STATUS_T mp4_read_box_soun_wave( VC_CONTAINER_T *p_ctx, int64_t size ); + +static struct { + const MP4_BOX_TYPE_T type; + VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T *, int64_t ); + const MP4_BOX_TYPE_T parent_type; +} mp4_box_list[] = +{ + {MP4_BOX_TYPE_FTYP, mp4_read_box_ftyp, MP4_BOX_TYPE_ROOT}, + {MP4_BOX_TYPE_MDAT, 0, MP4_BOX_TYPE_ROOT}, + {MP4_BOX_TYPE_MOOV, mp4_read_box_moov, MP4_BOX_TYPE_ROOT}, + {MP4_BOX_TYPE_MVHD, mp4_read_box_mvhd, MP4_BOX_TYPE_MOOV}, + {MP4_BOX_TYPE_TRAK, mp4_read_box_trak, MP4_BOX_TYPE_MOOV}, + {MP4_BOX_TYPE_TKHD, mp4_read_box_tkhd, MP4_BOX_TYPE_TRAK}, + {MP4_BOX_TYPE_MDIA, mp4_read_box_mdia, MP4_BOX_TYPE_TRAK}, + {MP4_BOX_TYPE_MDHD, mp4_read_box_mdhd, MP4_BOX_TYPE_MDIA}, + {MP4_BOX_TYPE_HDLR, mp4_read_box_hdlr, MP4_BOX_TYPE_MDIA}, + {MP4_BOX_TYPE_MINF, mp4_read_box_minf, MP4_BOX_TYPE_MDIA}, + {MP4_BOX_TYPE_VMHD, mp4_read_box_vmhd, MP4_BOX_TYPE_MINF}, + {MP4_BOX_TYPE_SMHD, mp4_read_box_smhd, MP4_BOX_TYPE_MINF}, + {MP4_BOX_TYPE_DINF, mp4_read_box_dinf, MP4_BOX_TYPE_MINF}, + {MP4_BOX_TYPE_DREF, mp4_read_box_dref, MP4_BOX_TYPE_DINF}, + {MP4_BOX_TYPE_STBL, mp4_read_box_stbl, MP4_BOX_TYPE_MINF}, + {MP4_BOX_TYPE_STSD, mp4_read_box_stsd, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_STTS, mp4_read_box_stts, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_CTTS, mp4_read_box_ctts, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_STSC, mp4_read_box_stsc, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_STSZ, mp4_read_box_stsz, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_STCO, mp4_read_box_stco, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_CO64, mp4_read_box_co64, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_STSS, mp4_read_box_stss, MP4_BOX_TYPE_STBL}, + {MP4_BOX_TYPE_VIDE, mp4_read_box_vide, MP4_BOX_TYPE_STSD}, + {MP4_BOX_TYPE_SOUN, mp4_read_box_soun, MP4_BOX_TYPE_STSD}, + {MP4_BOX_TYPE_TEXT, mp4_read_box_text, MP4_BOX_TYPE_STSD}, + + /* Codec specific boxes */ + {MP4_BOX_TYPE_AVCC, mp4_read_box_vide_avcC, MP4_BOX_TYPE_VIDE}, + {MP4_BOX_TYPE_D263, mp4_read_box_vide_d263, MP4_BOX_TYPE_VIDE}, + {MP4_BOX_TYPE_ESDS, mp4_read_box_esds, MP4_BOX_TYPE_VIDE}, + {MP4_BOX_TYPE_DAMR, mp4_read_box_soun_damr, MP4_BOX_TYPE_SOUN}, + {MP4_BOX_TYPE_DAWP, mp4_read_box_soun_dawp, MP4_BOX_TYPE_SOUN}, + {MP4_BOX_TYPE_DEVC, mp4_read_box_soun_devc, MP4_BOX_TYPE_SOUN}, + {MP4_BOX_TYPE_WAVE, mp4_read_box_soun_wave, MP4_BOX_TYPE_SOUN}, + {MP4_BOX_TYPE_ESDS, mp4_read_box_esds, MP4_BOX_TYPE_SOUN}, + + {MP4_BOX_TYPE_UNKNOWN, 0, MP4_BOX_TYPE_UNKNOWN} +}; + +static struct { + const VC_CONTAINER_FOURCC_T type; + const VC_CONTAINER_FOURCC_T codec; + bool batch; +} mp4_codec_mapping[] = +{ + {VC_FOURCC('a','v','c','1'), VC_CONTAINER_CODEC_H264, 0}, + {VC_FOURCC('m','p','4','v'), VC_CONTAINER_CODEC_MP4V, 0}, + {VC_FOURCC('s','2','6','3'), VC_CONTAINER_CODEC_H263, 0}, + {VC_FOURCC('m','p','e','g'), VC_CONTAINER_CODEC_MP2V, 0}, + {VC_FOURCC('m','j','p','a'), VC_CONTAINER_CODEC_MJPEGA, 0}, + {VC_FOURCC('m','j','p','b'), VC_CONTAINER_CODEC_MJPEGB, 0}, + + {VC_FOURCC('j','p','e','g'), VC_CONTAINER_CODEC_JPEG, 0}, + + {VC_FOURCC('m','p','4','a'), VC_CONTAINER_CODEC_MP4A, 0}, + {VC_FOURCC('s','a','m','r'), VC_CONTAINER_CODEC_AMRNB, 0}, + {VC_FOURCC('s','a','w','b'), VC_CONTAINER_CODEC_AMRWB, 0}, + {VC_FOURCC('s','a','w','p'), VC_CONTAINER_CODEC_AMRWBP, 0}, + {VC_FOURCC('a','c','-','3'), VC_CONTAINER_CODEC_AC3, 0}, + {VC_FOURCC('e','c','-','3'), VC_CONTAINER_CODEC_EAC3, 0}, + {VC_FOURCC('s','e','v','c'), VC_CONTAINER_CODEC_EVRC, 0}, + {VC_FOURCC('e','v','r','c'), VC_CONTAINER_CODEC_EVRC, 0}, + {VC_FOURCC('s','q','c','p'), VC_CONTAINER_CODEC_QCELP, 0}, + {VC_FOURCC('a','l','a','w'), VC_CONTAINER_CODEC_ALAW, 1}, + {VC_FOURCC('u','l','a','w'), VC_CONTAINER_CODEC_MULAW, 1}, + {VC_FOURCC('t','w','o','s'), VC_CONTAINER_CODEC_PCM_SIGNED_BE, 1}, + {VC_FOURCC('s','o','w','t'), VC_CONTAINER_CODEC_PCM_SIGNED_LE, 1}, + + {0, 0}, +}; +static VC_CONTAINER_FOURCC_T mp4_box_type_to_codec(VC_CONTAINER_FOURCC_T type) +{ + int i; + for(i = 0; mp4_codec_mapping[i].type; i++ ) + if(mp4_codec_mapping[i].type == type) break; + return mp4_codec_mapping[i].codec; +} + +static bool codec_needs_batch_mode(VC_CONTAINER_FOURCC_T codec) +{ + int i; + for(i = 0; mp4_codec_mapping[i].codec; i++ ) + if(mp4_codec_mapping[i].codec == codec) break; + return mp4_codec_mapping[i].batch; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_header( VC_CONTAINER_T *p_ctx, int64_t size, + MP4_BOX_TYPE_T *box_type, int64_t *box_size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + int64_t offset = STREAM_POSITION(p_ctx); + + module->box_offset = offset; + + *box_size = _READ_U32(p_ctx); + *box_type = _READ_FOURCC(p_ctx); + if(!*box_type) return VC_CONTAINER_ERROR_CORRUPTED; + + if(*box_size == 1) *box_size = _READ_U64(p_ctx); + LOG_FORMAT(p_ctx, "- Box %4.4s, Size: %"PRIi64", Offset: %"PRIi64, + (const char *)box_type, *box_size, offset); + + /* Sanity check the box size */ + if(*box_size < 0 /* Shouldn't ever get that big */ || + /* Only the mdat box can really be massive */ + (*box_type != MP4_BOX_TYPE_MDAT && *box_size > MP4_MAX_BOX_SIZE)) + { + LOG_DEBUG(p_ctx, "box %4.4s has an invalid size (%"PRIi64")", + (const char *)box_type, *box_size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + +#if 0 + /* It is valid for a box to have a zero size (i.e unknown) if it is the last one */ + if(*box_size == 0 && size >= 0) *box_size = size; + else if(*box_size == 0) *box_size = INT64_C(-1); +#else + if(*box_size <= 0) + { + LOG_DEBUG(p_ctx, "box %4.4s has an invalid size (%"PRIi64")", + (const char *)box_type, *box_size); + return VC_CONTAINER_ERROR_CORRUPTED; + } +#endif + + /* Sanity check box size against parent */ + if(size >= 0 && *box_size > size) + { + LOG_DEBUG(p_ctx, "box %4.4s is bigger than it should (%"PRIi64" > %"PRIi64")", + (const char *)box_type, *box_size, size); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + *box_size -= (STREAM_POSITION(p_ctx) - offset); + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_data( VC_CONTAINER_T *p_ctx, + MP4_BOX_TYPE_T box_type, int64_t box_size, MP4_BOX_TYPE_T parent_type ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + int64_t offset = STREAM_POSITION(p_ctx); + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + unsigned int i; + + /* Check if the box is a recognised one */ + for(i = 0; mp4_box_list[i].type; i++) + if(mp4_box_list[i].type == box_type && + mp4_box_list[i].parent_type == parent_type) break; + if(mp4_box_list[i].type == MP4_BOX_TYPE_UNKNOWN) + for(i = 0; mp4_box_list[i].type; i++) + if(mp4_box_list[i].type == box_type) break; + + /* Sanity check that the box has the right parent */ + if(mp4_box_list[i].type != MP4_BOX_TYPE_UNKNOWN && + mp4_box_list[i].parent_type != MP4_BOX_TYPE_UNKNOWN && + parent_type != MP4_BOX_TYPE_UNKNOWN && parent_type != mp4_box_list[i].parent_type) + { + LOG_FORMAT(p_ctx, "Ignoring mis-placed box %4.4s", (const char *)&box_type); + goto skip; + } + + /* Sanity check that the element isn't too deeply nested */ + if(module->box_level >= 2 * MP4_MAX_BOX_LEVEL) + { + LOG_DEBUG(p_ctx, "box %4.4s is too deep. skipping", (const char *)&box_type); + goto skip; + } + + module->box_level++; + + /* Call the box specific parsing function */ + if(mp4_box_list[i].pf_func) + status = mp4_box_list[i].pf_func(p_ctx, box_size); + + module->box_level--; + + if(status != VC_CONTAINER_SUCCESS) + LOG_DEBUG(p_ctx, "box %4.4s appears to be corrupted (%i)", (const char *)&box_type, status); + + skip: + /* Skip the rest of the box */ + box_size -= (STREAM_POSITION(p_ctx) - offset); + if(box_size < 0) /* Check for overruns */ + { + /* Things have gone really bad here and we ended up reading past the end of the + * box. We could maybe try to be clever and recover by seeking back to the end + * of the box. However if we get there, the file is clearly corrupted so there's + * no guarantee it would work anyway. */ + LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of box %4.4s", + -box_size, (const char *)&box_type); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if(box_size) + LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread in box %4.4s", + box_size, (const char *)&box_type ); + + if(box_size < MP4_MAX_BOX_SIZE) box_size = SKIP_BYTES(p_ctx, box_size); + else SEEK(p_ctx, STREAM_POSITION(p_ctx) + box_size); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box( VC_CONTAINER_T *p_ctx, int64_t size, + MP4_BOX_TYPE_T parent_type ) +{ + VC_CONTAINER_STATUS_T status; + MP4_BOX_TYPE_T box_type; + int64_t box_size; + + status = mp4_read_box_header( p_ctx, size, &box_type, &box_size ); + if(status != VC_CONTAINER_SUCCESS) return status; + return mp4_read_box_data( p_ctx, box_type, box_size, parent_type ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_boxes( VC_CONTAINER_T *p_ctx, int64_t size, + MP4_BOX_TYPE_T type) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t offset = STREAM_POSITION(p_ctx); + bool unknown_size = size < 0; + + /* Read contained boxes */ + module->box_level++; + while(status == VC_CONTAINER_SUCCESS && + (unknown_size || size >= MP4_BOX_MIN_HEADER_SIZE)) + { + offset = STREAM_POSITION(p_ctx); + status = mp4_read_box(p_ctx, size, type); + if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset); + } + module->box_level--; + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_ftyp( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + module->brand = MP4_READ_FOURCC(p_ctx, "major_brand"); + MP4_SKIP_U32(p_ctx, "minor_version"); + while(size >= 4) MP4_SKIP_FOURCC(p_ctx, "compatible_brands"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_moov( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MOOV); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_mvhd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t version, i; + int64_t duration; + + version = MP4_READ_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + if(version) + { + MP4_SKIP_U64(p_ctx, "creation_time"); + MP4_SKIP_U64(p_ctx, "modification_time"); + module->timescale = MP4_READ_U32(p_ctx, "timescale"); + duration = MP4_READ_U64(p_ctx, "duration"); + } + else + { + MP4_SKIP_U32(p_ctx, "creation_time"); + MP4_SKIP_U32(p_ctx, "modification_time"); + module->timescale = MP4_READ_U32(p_ctx, "timescale"); + duration = MP4_READ_U32(p_ctx, "duration"); + } + + if(module->timescale) + p_ctx->duration = duration * 1000000 / module->timescale; + + MP4_SKIP_U32(p_ctx, "rate"); + MP4_SKIP_U16(p_ctx, "volume"); + MP4_SKIP_U16(p_ctx, "reserved"); + for(i = 0; i < 2; i++) MP4_SKIP_U32(p_ctx, "reserved"); + for(i = 0; i < 9; i++) MP4_SKIP_U32(p_ctx, "matrix"); + for(i = 0; i < 6; i++) MP4_SKIP_U32(p_ctx, "pre_defined"); + MP4_SKIP_U32(p_ctx, "next_track_ID"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_trak( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track; + + /* We have a new track. Allocate and initialise our track context */ + if(p_ctx->tracks_num >= MP4_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size = 8; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size = 12; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_CO64].entry_size = 8; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_CTTS].entry_size = 8; + + status = mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_TRAK); + + /* TODO: Sanity check track */ + + track->is_enabled = true; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + module->current_track++; + p_ctx->tracks_num++; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_tkhd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t i, version; + int64_t duration; + + version = MP4_READ_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + if(version) + { + MP4_SKIP_U64(p_ctx, "creation_time"); + MP4_SKIP_U64(p_ctx, "modification_time"); + MP4_SKIP_U32(p_ctx, "track_ID"); + MP4_SKIP_U32(p_ctx, "reserved"); + duration = MP4_READ_U64(p_ctx, "duration"); + } + else + { + MP4_SKIP_U32(p_ctx, "creation_time"); + MP4_SKIP_U32(p_ctx, "modification_time"); + MP4_SKIP_U32(p_ctx, "track_ID"); + MP4_SKIP_U32(p_ctx, "reserved"); + duration = MP4_READ_U32(p_ctx, "duration"); + } + + if(module->timescale) + duration = duration * 1000000 / module->timescale; + + for(i = 0; i < 2; i++) MP4_SKIP_U32(p_ctx, "reserved"); + MP4_SKIP_U16(p_ctx, "layer"); + MP4_SKIP_U16(p_ctx, "alternate_group"); + MP4_SKIP_U16(p_ctx, "volume"); + MP4_SKIP_U16(p_ctx, "reserved"); + for(i = 0; i < 9; i++) MP4_SKIP_U32(p_ctx, "matrix"); + + MP4_SKIP_U32(p_ctx, "width"); + MP4_SKIP_U32(p_ctx, "height"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_mdia( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MDIA); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_mdhd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + uint32_t version, timescale; + int64_t duration; + + version = MP4_READ_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + if(version) + { + MP4_SKIP_U64(p_ctx, "creation_time"); + MP4_SKIP_U64(p_ctx, "modification_time"); + timescale = MP4_READ_U32(p_ctx, "timescale"); + duration = MP4_READ_U64(p_ctx, "duration"); + } + else + { + MP4_SKIP_U32(p_ctx, "creation_time"); + MP4_SKIP_U32(p_ctx, "modification_time"); + timescale = MP4_READ_U32(p_ctx, "timescale"); + duration = MP4_READ_U32(p_ctx, "duration"); + } + + if(timescale) duration = duration * 1000000 / timescale; + track_module->timescale = timescale; + + MP4_SKIP_U16(p_ctx, "language"); /* ISO-639-2/T language code */ + MP4_SKIP_U16(p_ctx, "pre_defined"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_hdlr( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + uint32_t i, fourcc, string_size; + VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN; + + if(size <= 24) return VC_CONTAINER_ERROR_CORRUPTED; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + MP4_SKIP_U32(p_ctx, "pre-defined"); + + fourcc = MP4_READ_FOURCC(p_ctx, "handler_type"); + if(fourcc == MP4_BOX_TYPE_VIDE) es_type = VC_CONTAINER_ES_TYPE_VIDEO; + if(fourcc == MP4_BOX_TYPE_SOUN) es_type = VC_CONTAINER_ES_TYPE_AUDIO; + if(fourcc == MP4_BOX_TYPE_TEXT) es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; + track->format->es_type = es_type; + + for(i = 0; i < 3; i++) MP4_SKIP_U32(p_ctx, "reserved"); + + string_size = size; + if(module->brand == MP4_BRAND_QT) + string_size = MP4_READ_U8(p_ctx, "string_size"); + + if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; + if(string_size > size) string_size = size; + + MP4_SKIP_STRING(p_ctx, string_size, "name"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_minf( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MINF); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_vmhd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + MP4_SKIP_U16(p_ctx, "graphicsmode"); + MP4_SKIP_U16(p_ctx, "opcolor"); + MP4_SKIP_U16(p_ctx, "opcolor"); + MP4_SKIP_U16(p_ctx, "opcolor"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_smhd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + MP4_SKIP_U16(p_ctx, "balance"); + MP4_SKIP_U16(p_ctx, "reserved"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_dinf( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_DINF); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_dref( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + MP4_SKIP_U32(p_ctx, "entry_count"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stbl( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_STBL); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stsd( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + VC_CONTAINER_STATUS_T status; + MP4_BOX_TYPE_T box_type; + int64_t box_size; + uint32_t count; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + count = MP4_READ_U32(p_ctx, "entry_count"); + if(!count) return VC_CONTAINER_ERROR_CORRUPTED; + + status = mp4_read_box_header( p_ctx, size, &box_type, &box_size ); + if(status != VC_CONTAINER_SUCCESS) return status; + + track->format->codec = mp4_box_type_to_codec(box_type); + if(!track->format->codec) track->format->codec = box_type; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) box_type = MP4_BOX_TYPE_VIDE; + if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) box_type = MP4_BOX_TYPE_SOUN; + if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) box_type = MP4_BOX_TYPE_TEXT; + status = mp4_read_box_data( p_ctx, box_type, box_size, MP4_BOX_TYPE_STSD ); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* Special treatment for MPEG4 */ + if(track->format->codec == VC_CONTAINER_CODEC_MP4A) + { + switch (track->priv->module->object_type_indication) + { + case MP4_MPEG4_AAC_LC_OBJECT_TYPE: + case MP4_MPEG2_AAC_LC_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_MP4A; break; + case MP4_MP3_OBJECT_TYPE: + case MP4_MPEG1_AUDIO_OBJECT_TYPE: + case MP4_KTF_MP3_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_MPGA; break; + case MP4_SKT_EVRC_2V1_OBJECT_TYPE: + case MP4_SKT_EVRC_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_EVRC; break; + case MP4_3GPP2_QCELP_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_QCELP; break; + default: + track->format->codec = VC_CONTAINER_CODEC_UNKNOWN; break; + } + } + else if(track->format->codec == VC_CONTAINER_CODEC_MP4V) + { + switch (track->priv->module->object_type_indication) + { + case MP4_MPEG4_VISUAL_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_MP4V; break; + case MP4_JPEG_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_JPEG; break; + case MP4_MPEG2_SP_OBJECT_TYPE: + case MP4_MPEG2_SNR_OBJECT_TYPE: + case MP4_MPEG2_AAC_LC_OBJECT_TYPE: + case MP4_MPEG2_MP_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_MP2V; break; + case MP4_MPEG1_VISUAL_OBJECT_TYPE: + track->format->codec = VC_CONTAINER_CODEC_MP1V; break; + default: + track->format->codec = VC_CONTAINER_CODEC_UNKNOWN; break; + } + } + + /* For some codecs we process the samples in batches to be more efficient */ + if(codec_needs_batch_mode(track->format->codec)) + track->priv->module->samples_batch_size = MP4_MAX_SAMPLES_BATCH_SIZE; + + /* Fix-up some of the data */ + switch(track->format->codec) + { + case VC_CONTAINER_CODEC_ALAW: + case VC_CONTAINER_CODEC_MULAW: + track->format->type->audio.bits_per_sample = 8; + track->priv->module->sample_size = track->format->type->audio.channels; + break; + case VC_CONTAINER_CODEC_PCM_SIGNED_LE: + case VC_CONTAINER_CODEC_PCM_SIGNED_BE: + track->priv->module->sample_size = (track->format->type->audio.bits_per_sample + 7) / + 8 * track->format->type->audio.channels; + break; + case VC_CONTAINER_CODEC_MP4A: + /* samplerate / channels is sometimes invalid so sanity check it using the codec config data */ + if(track->format->extradata_size >= 2) + { + static unsigned int rate[] = + { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350 }; + unsigned int samplerate = 0, channels = 0; + uint8_t *p = track->format->extradata; + uint32_t index = (p[0] & 7) << 1 | (p[1] >> 7); + if(index == 15 && track->format->extradata_size >= 5) + { + samplerate = (p[1] & 0x7f) << 17 | (p[2] << 9) | (p[3] << 1) | (p[4] >> 7); + channels = (p[4] >> 3) & 15; + } + else if(index < 13) + { + samplerate = rate[index]; + channels = (p[1] >> 3) & 15;; + } + + if(samplerate && samplerate != track->format->type->audio.sample_rate && + 2 * samplerate != track->format->type->audio.sample_rate) + track->format->type->audio.sample_rate = samplerate; + if(channels && channels != track->format->type->audio.channels && + 2 * channels != track->format->type->audio.channels) + track->format->type->audio.channels = channels; + } + break; + default: break; + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_cache_table( VC_CONTAINER_T *p_ctx, MP4_SAMPLE_TABLE_T table, + uint32_t entries, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + uint32_t available_entries, entries_size; + + if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED; + + track_module->sample_table[table].offset = STREAM_POSITION(p_ctx); + track_module->sample_table[table].entries = entries; + + available_entries = size / track_module->sample_table[table].entry_size; + if(available_entries < entries) + { + LOG_DEBUG(p_ctx, "table has less entries than advertised (%i/%i)", available_entries, entries); + entries = available_entries; + } + + entries_size = entries * track_module->sample_table[table].entry_size; + size = vc_container_io_cache(p_ctx->priv->io, entries_size ); + if(size != entries_size) + { + available_entries = size / track_module->sample_table[table].entry_size; + LOG_DEBUG(p_ctx, "cached less table entries than advertised (%i/%i)", available_entries, entries); + track_module->sample_table[table].entries = available_entries; + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stts( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STTS, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_ctts( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_CTTS, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stsc( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSC, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stsz( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + track_module->sample_size = READ_U32(p_ctx, "sample_size"); + if(track_module->sample_size) return STREAM_STATUS(p_ctx); + + entries = MP4_READ_U32(p_ctx, "sample_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSZ, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stco( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STCO, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_co64( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_CO64, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_stss( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + uint32_t entries; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + entries = MP4_READ_U32(p_ctx, "entry_count"); + return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSS, entries, size ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_esds_descriptor_header(VC_CONTAINER_T *p_ctx, int64_t *size, + uint32_t *descriptor_length, uint8_t *descriptor_type) +{ + uint32_t byte, length = 0; + + if(*size <= 0) return VC_CONTAINER_ERROR_CORRUPTED; + + *descriptor_type = _READ_U8(p_ctx); + (*size)--; + + /* Read descriptor size */ + while(*size) + { + byte = _READ_U8(p_ctx); + (*size)--; + length = (length << 7) | (byte&0x7F); + if(!(byte & 0x80)) break; + } + + if(*size <= 0 || length > *size) + { + LOG_FORMAT(p_ctx, "esds descriptor is corrupted"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + *descriptor_length = length; + LOG_FORMAT(p_ctx, "esds descriptor %x, size %i", *descriptor_type, *descriptor_length); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_esds( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + VC_CONTAINER_STATUS_T status; + uint32_t descriptor_length; + uint8_t descriptor_type; + + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U24(p_ctx, "flags"); + + status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(descriptor_type == 0x3) /* ES descriptor */ + { + uint8_t flags; + + MP4_SKIP_U16(p_ctx, "es_id"); + flags = MP4_READ_U8(p_ctx, "flags"); + + if(flags & 0x80) /* Stream dependence */ + MP4_SKIP_U16(p_ctx, "depend_on_es_id"); + + if(flags & 0x40) /* URL */ + { + uint32_t url_size = MP4_READ_U8(p_ctx, "url_size"); + MP4_SKIP_STRING(p_ctx, url_size, "url"); + } + + if(flags & 0x20) /* OCR_stream*/ + MP4_SKIP_U16(p_ctx, "OCR_es_id"); + + status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + if(descriptor_type == 0x4) /* Decoder Config descriptor */ + { + track->priv->module->object_type_indication = MP4_READ_U8(p_ctx, "object_type_indication"); + MP4_SKIP_U8(p_ctx, "stream_type"); + MP4_SKIP_U24(p_ctx, "buffer_size_db"); + MP4_SKIP_U32(p_ctx, "max_bitrate"); + track->format->bitrate = MP4_READ_U32(p_ctx, "avg_bitrate"); + + if(size <= 0 || descriptor_length <= 13) return STREAM_STATUS(p_ctx); + + status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type); + if(status != VC_CONTAINER_SUCCESS) return status; + if(descriptor_type == 0x05 && descriptor_length) + { + status = vc_container_track_allocate_extradata(p_ctx, track, descriptor_length); + if(status != VC_CONTAINER_SUCCESS) return status; + track->format->extradata_size = MP4_READ_BYTES(p_ctx, track->format->extradata, descriptor_length); + } + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_vide( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int i; + + for(i = 0; i < 6; i++) MP4_SKIP_U8(p_ctx, "reserved"); + MP4_SKIP_U16(p_ctx, "data_reference_index"); + + MP4_SKIP_U16(p_ctx, "pre_defined"); + MP4_SKIP_U16(p_ctx, "reserved"); + for(i = 0; i < 3; i++) MP4_SKIP_U32(p_ctx, "pre_defined"); + track->format->type->video.width = MP4_READ_U16(p_ctx, "width"); + track->format->type->video.height = MP4_READ_U16(p_ctx, "height"); + MP4_SKIP_U32(p_ctx, "horizresolution"); /* dpi */ + MP4_SKIP_U32(p_ctx, "vertresolution"); /* dpi */ + MP4_SKIP_U32(p_ctx, "reserved"); + MP4_SKIP_U16(p_ctx, "frame_count"); + MP4_SKIP_BYTES(p_ctx, 32); + MP4_SKIP_U16(p_ctx, "depth"); + MP4_SKIP_U16(p_ctx, "pre_defined"); + + if(size > 0) + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_VIDE ); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_vide_avcC( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + VC_CONTAINER_STATUS_T status; + + if(track->format->codec != VC_CONTAINER_CODEC_H264 || size <= 0) + return VC_CONTAINER_ERROR_CORRUPTED; + + track->format->codec_variant = VC_FOURCC('a','v','c','C'); + + status = vc_container_track_allocate_extradata(p_ctx, track, (unsigned int)size); + if(status != VC_CONTAINER_SUCCESS) return status; + track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_vide_d263( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + MP4_SKIP_FOURCC(p_ctx, "vendor"); + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U8(p_ctx, "level"); + MP4_SKIP_U8(p_ctx, "profile"); + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_soun( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int i, version = 0; + + for(i = 0; i < 6; i++) MP4_SKIP_U8(p_ctx, "reserved"); + MP4_SKIP_U16(p_ctx, "data_reference_index"); + + version = MP4_READ_U16(p_ctx, "version"); + MP4_SKIP_U16(p_ctx, "revision_level"); + MP4_SKIP_U32(p_ctx, "vendor"); + + track->format->type->audio.channels = MP4_READ_U16(p_ctx, "channelcount"); + track->format->type->audio.bits_per_sample = MP4_READ_U16(p_ctx, "samplesize"); + MP4_SKIP_U16(p_ctx, "pre_defined"); + MP4_SKIP_U16(p_ctx, "reserved"); + track->format->type->audio.sample_rate = MP4_READ_U16(p_ctx, "samplerate"); + MP4_SKIP_U16(p_ctx, "samplerate_fp_low"); + + if(version == 1) + { + MP4_SKIP_U32(p_ctx, "samples_per_packet"); + MP4_SKIP_U32(p_ctx, "bytes_per_packet"); + MP4_SKIP_U32(p_ctx, "bytes_per_frame"); + MP4_SKIP_U32(p_ctx, "bytes_per_sample"); + } + + if(size > 0) + return mp4_read_box( p_ctx, size, MP4_BOX_TYPE_SOUN ); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_soun_damr( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + + MP4_SKIP_FOURCC(p_ctx, "vendor"); + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U8(p_ctx, "mode_set"); + MP4_SKIP_U8(p_ctx, "mode_change_period"); + MP4_SKIP_U8(p_ctx, "frame_per_second"); + + track->format->type->audio.channels = 1; + if(track->format->codec == VC_CONTAINER_CODEC_AMRNB) + track->format->type->audio.sample_rate = 8000; + else if(track->format->codec == VC_CONTAINER_CODEC_AMRWB) + track->format->type->audio.sample_rate = 16000; + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_soun_dawp( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + + MP4_SKIP_FOURCC(p_ctx, "vendor"); + MP4_SKIP_U8(p_ctx, "version"); + + track->format->type->audio.channels = 2; + track->format->type->audio.sample_rate = 16000; + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_soun_devc( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + + MP4_SKIP_FOURCC(p_ctx, "vendor"); + MP4_SKIP_U8(p_ctx, "version"); + MP4_SKIP_U8(p_ctx, "samples_per_frame"); + + track->format->type->audio.channels = 1; + track->format->type->audio.sample_rate = 8000; + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_soun_wave( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_SOUN); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_box_text( VC_CONTAINER_T *p_ctx, int64_t size ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(module); + + /* TODO */if(1) return VC_CONTAINER_ERROR_FAILED; + + if(size > 0) + return mp4_read_box( p_ctx, size, MP4_BOX_TYPE_TEXT ); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +#ifdef ENABLE_MP4_READER_LOG_STATE +static void mp4_log_state( VC_CONTAINER_T *p_ctx, MP4_READER_STATE_T *state ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + LOG_DEBUG(p_ctx, "state:"); + LOG_DEBUG(p_ctx, "duration: %i, pts %i, dts %i", (int)state->duration, + (int)state->pts, (int)state->dts); + LOG_DEBUG(p_ctx, "sample: %i, offset %i, sample_offset %i, sample_size %i", + state->sample, (int)state->offset, state->sample_offset, + state->sample_size); + LOG_DEBUG(p_ctx, "sample_duration: %i, count %i", + state->sample_duration, state->sample_duration_count); + LOG_DEBUG(p_ctx, "sample_composition_offset: %i, count %i", + state->sample_composition_offset, state->sample_composition_count); + LOG_DEBUG(p_ctx, "next_sync_sample: %i, keyframe %i", + state->next_sync_sample, state->keyframe); + LOG_DEBUG(p_ctx, "samples_per_chunk: %i, chunks %i, samples_in_chunk %i", + state->samples_per_chunk, state->chunks, state->samples_in_chunk); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STTS %i", state->sample_table[MP4_SAMPLE_TABLE_STTS].entry); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STSZ %i", state->sample_table[MP4_SAMPLE_TABLE_STSZ].entry); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STSC %i", state->sample_table[MP4_SAMPLE_TABLE_STSC].entry); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STCO %i", state->sample_table[MP4_SAMPLE_TABLE_STCO].entry); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_CO64 %i", state->sample_table[MP4_SAMPLE_TABLE_CO64].entry); + LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_CTTS %i", state->sample_table[MP4_SAMPLE_TABLE_CTTS].entry); +} +#endif /* ENABLE_MP4_READER_LOG_STATE */ + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_seek_sample_table( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *track_module, MP4_READER_STATE_T *state, + MP4_SAMPLE_TABLE_T table) +{ + int64_t seek_offset; + + /* Seek to the next entry in the table */ + if(state->sample_table[table].entry >= track_module->sample_table[table].entries) + return VC_CONTAINER_ERROR_EOS; + + seek_offset = track_module->sample_table[table].offset + + track_module->sample_table[table].entry_size * state->sample_table[table].entry; + + return SEEK(p_ctx, seek_offset); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_sample_table( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *track_module, MP4_READER_STATE_T *state, + MP4_SAMPLE_TABLE_T table, unsigned int seek) +{ + uint32_t value; + + if(table == MP4_SAMPLE_TABLE_STSZ && track_module->sample_size) + { + state->sample_size = track_module->sample_size; + return state->status; + } + + /* CO64 support */ + if(table == MP4_SAMPLE_TABLE_STCO && + track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries) + table = MP4_SAMPLE_TABLE_CO64; + + /* Seek to the next entry in the table */ + if(seek) + { + state->status = mp4_seek_sample_table( p_ctx, track_module, state, table ); + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + } + + switch(table) + { + case MP4_SAMPLE_TABLE_STSZ: + state->sample_size = _READ_U32(p_ctx); + state->status = STREAM_STATUS(p_ctx); + break; + + case MP4_SAMPLE_TABLE_STTS: + state->sample_duration_count = _READ_U32(p_ctx); + state->sample_duration = _READ_U32(p_ctx); + state->status = STREAM_STATUS(p_ctx); + if(state->status != VC_CONTAINER_SUCCESS) break; + if(!state->sample_duration_count) state->status = VC_CONTAINER_ERROR_CORRUPTED; + break; + + case MP4_SAMPLE_TABLE_CTTS: + state->sample_composition_count = _READ_U32(p_ctx); + state->sample_composition_offset = _READ_U32(p_ctx); /* Converted to signed */ + state->status = STREAM_STATUS(p_ctx); + if(state->status != VC_CONTAINER_SUCCESS) break; + if(!state->sample_composition_count) state->status = VC_CONTAINER_ERROR_CORRUPTED; + break; + + case MP4_SAMPLE_TABLE_STSC: + state->chunks = _READ_U32(p_ctx); + state->samples_per_chunk = _READ_U32(p_ctx); + _SKIP_U32(p_ctx); + state->status = STREAM_STATUS(p_ctx); + if(state->status != VC_CONTAINER_SUCCESS) break; + + if(state->sample_table[table].entry + 1 < + track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries) value = _READ_U32(p_ctx); + else value = -1; + + if(!state->chunks || !state->samples_per_chunk || state->chunks >= value ) + {state->status = VC_CONTAINER_ERROR_CORRUPTED; break;} + state->chunks = value - state->chunks; + state->samples_in_chunk = state->samples_per_chunk; + break; + + case MP4_SAMPLE_TABLE_STCO: + case MP4_SAMPLE_TABLE_CO64: + state->offset = table == MP4_SAMPLE_TABLE_STCO ? _READ_U32(p_ctx) : _READ_U64(p_ctx); + state->status = STREAM_STATUS(p_ctx); + if(state->status != VC_CONTAINER_SUCCESS) break; + if(!state->offset) state->status = VC_CONTAINER_ERROR_CORRUPTED; + state->samples_in_chunk = state->samples_per_chunk; + break; + + case MP4_SAMPLE_TABLE_STSS: + state->next_sync_sample = _READ_U32(p_ctx); + state->status = STREAM_STATUS(p_ctx); + break; + + default: break; + } + + state->sample_table[table].entry++; + return state->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_sample_header( VC_CONTAINER_T *p_ctx, uint32_t track, + MP4_READER_STATE_T *state ) +{ + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; + + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + + if(state->sample_offset < state->sample_size) + return state->status; /* We still have data left from the current sample */ + + /* Switch to the next sample */ + state->offset += state->sample_size; + state->sample_offset = 0; + state->sample_size = 0; + state->sample++; + + if(!state->samples_in_chunk) + { + /* We're switching to the next chunk */ + if(!state->chunks) + { + /* Seek to the next entry in the STSC */ + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSC, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + } + + /* Get the offset of the new chunk */ + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STCO, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + state->chunks--; + } + state->samples_in_chunk--; + + /* Get the new sample size */ + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + /* Get the timestamp */ + if(track_module->timescale) + state->pts = state->dts = state->duration * 1000000 / track_module->timescale; + if(!state->sample_duration_count) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + } + state->sample_duration_count--; + + /* Get the composition time */ + if(track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries) + { + if(!state->sample_composition_count) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_CTTS, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + } + if(track_module->timescale) + state->pts = (state->duration + state->sample_composition_offset) * 1000000 / track_module->timescale; + state->sample_composition_count--; + } + state->duration += state->sample_duration; + + /* Get the keyframe flag */ + if(state->sample_table[MP4_SAMPLE_TABLE_STSS].entry < + track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries && + !state->next_sync_sample) + { + mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSS, 1 ); + state->status = VC_CONTAINER_SUCCESS; /* This isn't a critical error */ + } + + state->keyframe = + track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries && + state->sample == state->next_sync_sample; + if(state->keyframe) + state->next_sync_sample = 0; + + /* Try to batch several samples together if requested. We'll always stop at the chunk boundary */ + if(track_module->samples_batch_size) + { + uint32_t size = state->sample_size; + while(state->samples_in_chunk && size < track_module->samples_batch_size) + { + if(mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, 1 )) break; + + if(!state->sample_duration_count) + if(mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, 1 )) break; + + state->sample_duration_count--; + state->duration += state->sample_duration; + + size += state->sample_size; + state->samples_in_chunk--; + state->sample++; + } + state->sample_size = size; + } + +#ifdef ENABLE_MP4_READER_LOG_STATE + mp4_log_state(p_ctx, state); +#endif + + error: + return state->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_read_sample_data( VC_CONTAINER_T *p_ctx, uint32_t track, + MP4_READER_STATE_T *state, uint8_t *data, unsigned int *data_size ) +{ + VC_CONTAINER_STATUS_T status; + unsigned int size = state->sample_size - state->sample_offset; + + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + + if(data_size && *data_size < size) size = *data_size; + + if(data) + { + state->status = SEEK(p_ctx, state->offset + state->sample_offset); + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + + size = READ_BYTES(p_ctx, data, size); + } + state->sample_offset += size; + + if(data_size) *data_size = size; + state->status = STREAM_STATUS(p_ctx); + if(state->status != VC_CONTAINER_SUCCESS) return state->status; + + status = state->status; + + /* Switch to the start of the next sample */ + if(state->sample_offset >= state->sample_size) + mp4_read_sample_header(p_ctx, track, state); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_TRACK_MODULE_T *track_module; + VC_CONTAINER_STATUS_T status; + MP4_READER_STATE_T *state; + uint32_t i, track; + unsigned int data_size; + uint8_t *data = 0; + int64_t offset; + + /* Select the track to read from. If no specific track is requested by the caller, this + * will be the track to which the next bit of data in the mdat belongs to */ + if(!(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)) + { + for(i = 0, track = 0, offset = -1; i < p_ctx->tracks_num; i++) + { + track_module = p_ctx->tracks[i]->priv->module; + + /* Ignore tracks which have no more readable data */ + if(track_module->state.status != VC_CONTAINER_SUCCESS) continue; + + if(offset >= 0 && track_module->state.offset >= offset) continue; + offset = track_module->state.offset; + track = i; + } + } + else track = packet->track; + + if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + track_module = p_ctx->tracks[track]->priv->module; + state = &track_module->state; + + status = mp4_read_sample_header(p_ctx, track, state); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(!packet) /* Skip packet */ + return mp4_read_sample_data(p_ctx, track, state, 0, 0); + + packet->dts = state->dts; + packet->pts = state->pts; + packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + if(state->keyframe) packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + if(!state->sample_offset) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + packet->track = track; + packet->frame_size = state->sample_size; + packet->size = state->sample_size - state->sample_offset; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + return mp4_read_sample_data(p_ctx, track, state, 0, 0); + else if((flags & VC_CONTAINER_READ_FLAG_INFO) || !packet->data) + return VC_CONTAINER_SUCCESS; + + data = packet->data; + data_size = packet->buffer_size; + + status = mp4_read_sample_data(p_ctx, track, state, data, &data_size); + if(status != VC_CONTAINER_SUCCESS) + { + /* FIXME */ + return status; + } + + packet->size = data_size; + if(state->sample_offset) //? + packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + return status; +} + +/*****************************************************************************/ +static uint32_t mp4_find_sample( VC_CONTAINER_T *p_ctx, uint32_t track, + MP4_READER_STATE_T *state, int64_t seek_time, VC_CONTAINER_STATUS_T *p_status ) +{ + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t sample = 0, sample_duration_count; + int64_t sample_duration, seek_time_up = seek_time + 1; + unsigned int i; + VC_CONTAINER_PARAM_UNUSED(state); + + seek_time = seek_time * track_module->timescale / 1000000; + /* We also need to check against the time rounded up to account for + * rounding errors in the timestamp (because of the timescale conversion) */ + seek_time_up = seek_time_up * track_module->timescale / 1000000; + + status = SEEK(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].offset); + if(status != VC_CONTAINER_SUCCESS) goto end; + + /* Find the sample which corresponds to the requested time */ + for(i = 0; i < track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries; i++) + { + sample_duration_count = _READ_U32(p_ctx); + sample_duration = _READ_U32(p_ctx); + status = STREAM_STATUS(p_ctx); + if(status != VC_CONTAINER_SUCCESS) break; + + if(sample_duration_count * sample_duration <= seek_time) + { + seek_time -= sample_duration_count * sample_duration; + seek_time_up -= sample_duration_count * sample_duration; + sample += sample_duration_count; + continue; + } + if(!sample_duration) break; + + seek_time /= sample_duration; + seek_time_up /= sample_duration; + sample += MAX(seek_time, seek_time_up); + break; + } + + end: + if(p_status) *p_status = status; + return sample; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_seek_track( VC_CONTAINER_T *p_ctx, uint32_t track, + MP4_READER_STATE_T *state, uint32_t sample ) +{ + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module; + uint32_t chunk = 0, samples; + unsigned int i; + + memset(state, 0, sizeof(*state)); + + /* Find the right chunk */ + for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries; i++) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSC, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + if(state->chunks * state->samples_per_chunk <= samples) + { + samples -= state->chunks * state->samples_per_chunk; + chunk += state->chunks; + continue; + } + + while(samples >= state->samples_per_chunk) + { + samples -= state->samples_per_chunk; + state->chunks--; + chunk++; + } + + state->chunks--; + break; + } + + /* Get the offset of the selected chunk */ + state->sample_table[MP4_SAMPLE_TABLE_STCO].entry = chunk; + state->sample_table[MP4_SAMPLE_TABLE_CO64].entry = chunk; + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STCO, 1 ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + /* Find the sample offset within the chunk */ + state->sample_table[MP4_SAMPLE_TABLE_STSZ].entry = sample - samples; + for(i = 0; i < samples; i++) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, !i ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + state->offset += state->sample_size; + state->samples_in_chunk--; + } + + /* Get the timestamp */ + for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries; i++) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, !i ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + if(state->sample_duration_count <= samples) + { + samples -= state->sample_duration_count; + state->duration += state->sample_duration * state->sample_duration_count; + continue; + } + + state->sample_duration_count -= samples; + state->duration += samples * state->sample_duration; + break; + } + + /* Find the right place in the sample composition table */ + for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries; i++) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_CTTS, !i ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + if(state->sample_composition_count <= samples) + { + samples -= state->sample_composition_count; + continue; + } + + state->sample_composition_count -= samples; + break; + } + + /* Find the right place in the synchronisation table */ + for(i = 0; i < track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries; i++) + { + state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSS, !i ); + if(state->status != VC_CONTAINER_SUCCESS) goto error; + + if(state->next_sync_sample >= sample + 1) break; + } + + state->sample = sample; + state->sample_size = 0; + mp4_read_sample_header(p_ctx, track, state); + + error: + return state->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_reader_seek(VC_CONTAINER_T *p_ctx, + int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module; + VC_CONTAINER_STATUS_T status; + uint32_t i, track, sample, prev_sample, next_sample; + int64_t seek_time = *offset; + VC_CONTAINER_PARAM_UNUSED(module); + VC_CONTAINER_PARAM_UNUSED(mode); + + /* Reset the states */ + for(i = 0; i < p_ctx->tracks_num; i++) + memset(&p_ctx->tracks[i]->priv->module->state, 0, sizeof(p_ctx->tracks[i]->priv->module->state)); + + /* Deal with the easy case first */ + if(!*offset) + { + /* Initialise tracks */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + /* FIXME: we should check we've got at least one success */ + mp4_read_sample_header(p_ctx, i, &p_ctx->tracks[i]->priv->module->state); + } + return VC_CONTAINER_SUCCESS; + } + + /* Find the first enabled video track */ + for(track = 0; track < p_ctx->tracks_num; track++) + if(p_ctx->tracks[track]->is_enabled && + p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break; + if(track == p_ctx->tracks_num) goto seek_time_found; /* No video track found */ + track_module = p_ctx->tracks[track]->priv->module; + + /* Find the sample number for the requested time */ + sample = mp4_find_sample( p_ctx, track, &track_module->state, seek_time, &status ); + if(status != VC_CONTAINER_SUCCESS) goto seek_time_found; + + /* Find the closest sync sample */ + status = mp4_seek_sample_table( p_ctx, track_module, &track_module->state, MP4_SAMPLE_TABLE_STSS ); + if(status != VC_CONTAINER_SUCCESS) goto seek_time_found; + for(i = 0, prev_sample = 0, next_sample = 0; + i < track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries; i++) + { + next_sample = _READ_U32(p_ctx) - 1; + if(next_sample > sample) + { + sample = (flags & VC_CONTAINER_SEEK_FLAG_FORWARD) ? next_sample : prev_sample; + break; + } + prev_sample = next_sample; + } + + /* Do the seek on this track and use its timestamp as the new seek point */ + status = mp4_seek_track(p_ctx, track, &track_module->state, sample); + if(status != VC_CONTAINER_SUCCESS) goto seek_time_found; + seek_time = track_module->state.pts; + + seek_time_found: + + for(i = 0; i < p_ctx->tracks_num; i++) + { + uint32_t sample; + track_module = p_ctx->tracks[i]->priv->module; + if(track_module->state.offset) continue; + sample = mp4_find_sample( p_ctx, i, &track_module->state, seek_time, &status ); + if(status != VC_CONTAINER_SUCCESS) return status; //FIXME + + status = mp4_seek_track(p_ctx, i, &track_module->state, sample); + } + + *offset = seek_time; + return VC_CONTAINER_SUCCESS; +} + +/****************************************************************************** +Global function definitions. +******************************************************************************/ + +VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + VC_CONTAINER_MODULE_T *module = 0; + unsigned int i; + uint8_t h[8]; + + /* Check for a known box type to see if we're dealing with mp4 */ + if( PEEK_BYTES(p_ctx, h, 8) != 8 ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + switch(VC_FOURCC(h[4],h[5],h[6],h[7])) + { + case MP4_BOX_TYPE_FTYP: + case MP4_BOX_TYPE_MDAT: + case MP4_BOX_TYPE_MOOV: + case MP4_BOX_TYPE_FREE: + case MP4_BOX_TYPE_SKIP: + case MP4_BOX_TYPE_WIDE: + case MP4_BOX_TYPE_PNOT: + case MP4_BOX_TYPE_PICT: + case MP4_BOX_TYPE_UDTA: + case MP4_BOX_TYPE_UUID: + break; + default: + /* Couldn't recognize the box type. This doesn't look like an mp4. */ + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + /* + * We are dealing with an MP4 file + */ + + LOG_DEBUG(p_ctx, "using mp4 reader"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + + while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS) + { + MP4_BOX_TYPE_T box_type; + int64_t box_size; + + status = mp4_read_box_header( p_ctx, INT64_C(-1), &box_type, &box_size ); + if(status != VC_CONTAINER_SUCCESS) goto error; + + if(box_type == MP4_BOX_TYPE_MDAT) + { + module->data_offset = STREAM_POSITION(p_ctx); + module->data_size = box_size; + if(module->found_moov) break; /* We've got everything we want */ + } + else if(box_type == MP4_BOX_TYPE_MOOV) + module->found_moov = true; + + status = mp4_read_box_data( p_ctx, box_type, box_size, MP4_BOX_TYPE_ROOT ); + if(status != VC_CONTAINER_SUCCESS) goto error; + + if(module->found_moov && module->data_offset) break; /* We've got everything we want */ + } + + /* Initialise tracks */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + /* FIXME: we should check we've got at least one success */ + status = mp4_read_sample_header(p_ctx, i, &p_ctx->tracks[i]->priv->module->state); + } + + status = SEEK(p_ctx, module->data_offset); + if(status != VC_CONTAINER_SUCCESS) goto error; + + p_ctx->priv->pf_close = mp4_reader_close; + p_ctx->priv->pf_read = mp4_reader_read; + p_ctx->priv->pf_seek = mp4_reader_seek; + + if(STREAM_SEEKABLE(p_ctx)) + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "mp4: error opening stream"); + if(module) mp4_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open mp4_reader_open +#endif diff --git a/containers/mp4/mp4_writer.c b/containers/mp4/mp4_writer.c new file mode 100755 index 0000000..5b33e01 --- /dev/null +++ b/containers/mp4/mp4_writer.c @@ -0,0 +1,1441 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_writer_utils.h" +#include "containers/core/containers_logging.h" +#include "containers/mp4/mp4_common.h" +#undef CONTAINER_HELPER_LOG_INDENT +#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->box_level + +VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T *p_ctx ); + +/****************************************************************************** +Defines. +******************************************************************************/ +#define MP4_TRACKS_MAX 16 +#define MP4_TIMESCALE 1000 + +#define MP4_64BITS_TIME 0 /* 0 to disable / 1 to enable */ + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + uint32_t fourcc; + uint32_t samples; + uint32_t chunks; + + int64_t offset; + int64_t timestamp; + int64_t delta_timestamp; + int64_t samples_in_chunk; + int64_t samples_in_prev_chunk; + struct { + uint32_t entries; + uint32_t entry_size; + } sample_table[MP4_SAMPLE_TABLE_NUM]; + + int64_t first_pts; + int64_t last_pts; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + int box_level; + MP4_BRAND_T brand; + + VC_CONTAINER_TRACK_T *tracks[MP4_TRACKS_MAX]; + bool tracks_add_done; + + VC_CONTAINER_WRITER_EXTRAIO_T null; + + unsigned int current_track; + + unsigned moov_size; + int64_t mdat_offset; + int64_t data_offset; + + uint32_t samples; + VC_CONTAINER_WRITER_EXTRAIO_T temp; + VC_CONTAINER_PACKET_T sample; + int64_t sample_offset; + int64_t prev_sample_dts; + + int64_t duration; + /**/ + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Static functions within this file. +******************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_extended( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T box_type, uint32_t fourcc ); +static VC_CONTAINER_STATUS_T mp4_write_box( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T box_type ); +static VC_CONTAINER_STATUS_T mp4_write_box_ftyp( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_moov( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_mvhd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_trak( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_tkhd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_mdia( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_mdhd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_hdlr( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_minf( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_vmhd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_smhd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_dinf( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_dref( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stbl( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stsd( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stts( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_ctts( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stsc( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stsz( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stco( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_co64( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_stss( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_vide( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_soun( VC_CONTAINER_T *p_ctx ); +static VC_CONTAINER_STATUS_T mp4_write_box_esds( VC_CONTAINER_T *p_ctx ); + +static struct { + const MP4_BOX_TYPE_T type; + VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T * ); +} mp4_box_list[] = +{ + {MP4_BOX_TYPE_FTYP, mp4_write_box_ftyp}, + {MP4_BOX_TYPE_MOOV, mp4_write_box_moov}, + {MP4_BOX_TYPE_MVHD, mp4_write_box_mvhd}, + {MP4_BOX_TYPE_TRAK, mp4_write_box_trak}, + {MP4_BOX_TYPE_TKHD, mp4_write_box_tkhd}, + {MP4_BOX_TYPE_MDIA, mp4_write_box_mdia}, + {MP4_BOX_TYPE_MDHD, mp4_write_box_mdhd}, + {MP4_BOX_TYPE_HDLR, mp4_write_box_hdlr}, + {MP4_BOX_TYPE_MINF, mp4_write_box_minf}, + {MP4_BOX_TYPE_VMHD, mp4_write_box_vmhd}, + {MP4_BOX_TYPE_SMHD, mp4_write_box_smhd}, + {MP4_BOX_TYPE_DINF, mp4_write_box_dinf}, + {MP4_BOX_TYPE_DREF, mp4_write_box_dref}, + {MP4_BOX_TYPE_STBL, mp4_write_box_stbl}, + {MP4_BOX_TYPE_STSD, mp4_write_box_stsd}, + {MP4_BOX_TYPE_STTS, mp4_write_box_stts}, + {MP4_BOX_TYPE_CTTS, mp4_write_box_ctts}, + {MP4_BOX_TYPE_STSC, mp4_write_box_stsc}, + {MP4_BOX_TYPE_STSZ, mp4_write_box_stsz}, + {MP4_BOX_TYPE_STCO, mp4_write_box_stco}, + {MP4_BOX_TYPE_CO64, mp4_write_box_co64}, + {MP4_BOX_TYPE_STSS, mp4_write_box_stss}, + {MP4_BOX_TYPE_VIDE, mp4_write_box_vide}, + {MP4_BOX_TYPE_SOUN, mp4_write_box_soun}, + {MP4_BOX_TYPE_ESDS, mp4_write_box_esds}, + {MP4_BOX_TYPE_UNKNOWN, 0} +}; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_extended( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T type, uint32_t fourcc ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t box_size = 0; + unsigned int i; + + /* Find out which object we want to write */ + for( i = 0; mp4_box_list[i].type && mp4_box_list[i].type != type; i++ ); + + /* Check we found the requested type */ + if(!mp4_box_list[i].type) + { + vc_container_assert(0); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* We need to find out the size of the object we're going to write it. */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null)) + { + status = mp4_write_box_extended( p_ctx, type, fourcc ); + box_size = STREAM_POSITION(p_ctx); + } + vc_container_writer_extraio_disable(p_ctx, &module->null); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* Write the object header */ + LOG_FORMAT(p_ctx, "- Box %4.4s, size: %"PRIi64, (const char *)&fourcc, box_size); + _WRITE_U32(p_ctx, (uint32_t)box_size); + _WRITE_FOURCC(p_ctx, fourcc); + + module->box_level++; + + /* Call the object specific writing function */ + status = mp4_box_list[i].pf_func(p_ctx); + + module->box_level--; + + if(status != VC_CONTAINER_SUCCESS) + LOG_DEBUG(p_ctx, "box %4.4s appears to be corrupted", (char *)mp4_box_list[i].type); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T type ) +{ + return mp4_write_box_extended( p_ctx, type, type ); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_ftyp( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + WRITE_FOURCC(p_ctx, module->brand, "major_brand"); + WRITE_U32(p_ctx, 512, "minor_version"); + if(module->brand == MP4_BRAND_QT) + { + WRITE_FOURCC(p_ctx, MP4_BRAND_QT, "compatible_brands"); + return STREAM_STATUS(p_ctx); + } + + if(module->brand == MP4_BRAND_SKM2) + WRITE_FOURCC(p_ctx, MP4_BRAND_SKM2, "compatible_brands"); + WRITE_FOURCC(p_ctx, MP4_BRAND_ISOM, "compatible_brands"); + WRITE_FOURCC(p_ctx, MP4_BRAND_MP42, "compatible_brands"); + WRITE_FOURCC(p_ctx, MP4_BRAND_3GP4, "compatible_brands"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_moov( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MVHD); + if(status != VC_CONTAINER_SUCCESS) return status; + + for(i = 0; i < p_ctx->tracks_num; i++) + { + module->current_track = i; + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_TRAK); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_mvhd( VC_CONTAINER_T *p_ctx ) +{ + static uint32_t matrix[] = { 0x10000,0,0,0,0x10000,0,0,0,0x40000000 }; + unsigned int version = MP4_64BITS_TIME; + unsigned int i; + + WRITE_U8(p_ctx, version, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + /**/ + p_ctx->duration = 0; + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; + VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module; + int64_t track_duration = track_module->last_pts - track_module->first_pts; + if(track_duration > p_ctx->duration) + p_ctx->duration = track_duration; + } + + if(version) + { + WRITE_U64(p_ctx, 0, "creation_time"); + WRITE_U64(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); + WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + else + { + WRITE_U32(p_ctx, 0, "creation_time"); + WRITE_U32(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); + WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + + WRITE_U32(p_ctx, 0x10000, "rate"); /* 1.0 */ + WRITE_U16(p_ctx, 0x100, "volume"); /* full volume */ + WRITE_U16(p_ctx, 0, "reserved"); + for(i = 0; i < 2; i++) + WRITE_U32(p_ctx, 0, "reserved"); + for(i = 0; i < 9; i++) /* unity matrix */ + WRITE_U32(p_ctx, matrix[i], "matrix"); + for(i = 0; i < 6; i++) + WRITE_U32(p_ctx, 0, "pre_defined"); + WRITE_U32(p_ctx, p_ctx->tracks_num + 1, "next_track_ID"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_trak( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_TKHD); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MDIA); + if(status != VC_CONTAINER_SUCCESS) return status; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_tkhd( VC_CONTAINER_T *p_ctx ) +{ + static uint32_t matrix[] = { 0x10000,0,0,0,0x10000,0,0,0,0x40000000 }; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int version = MP4_64BITS_TIME; + uint32_t i, width = 0, height = 0; + + WRITE_U8(p_ctx, version, "version"); + WRITE_U24(p_ctx, 0x7, "flags"); /* track enabled */ + + if(version) + { + WRITE_U64(p_ctx, 0, "creation_time"); + WRITE_U64(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, module->current_track + 1, "track_ID"); + WRITE_U32(p_ctx, 0, "reserved"); + WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + else + { + WRITE_U32(p_ctx, 0, "creation_time"); + WRITE_U32(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, module->current_track + 1, "track_ID"); + WRITE_U32(p_ctx, 0, "reserved"); + WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + + for(i = 0; i < 2; i++) + WRITE_U32(p_ctx, 0, "reserved"); + WRITE_U16(p_ctx, 0, "layer"); + WRITE_U16(p_ctx, 0, "alternate_group"); + WRITE_U16(p_ctx, track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO ? 0x100 : 0, "volume"); + WRITE_U16(p_ctx, 0, "reserved"); + for(i = 0; i < 9; i++) /* unity matrix */ + WRITE_U32(p_ctx, matrix[i], "matrix"); + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + width = track->format->type->video.width << 16; + height = track->format->type->video.height << 16; + if(track->format->type->video.par_num && track->format->type->video.par_den) + width = width * (uint64_t)track->format->type->video.par_num / + track->format->type->video.par_den; + } + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) + { + /* FIXME */ + } + + WRITE_U32(p_ctx, width, "width"); + WRITE_U32(p_ctx, height, "height"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_mdia( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MDHD); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_HDLR); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MINF); + if(status != VC_CONTAINER_SUCCESS) return status; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_mdhd( VC_CONTAINER_T *p_ctx ) +{ + unsigned int version = MP4_64BITS_TIME; + + WRITE_U8(p_ctx, version, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + // FIXME: take a better timescale ?? + if(version) + { + WRITE_U64(p_ctx, 0, "creation_time"); + WRITE_U64(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); + WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + else + { + WRITE_U32(p_ctx, 0, "creation_time"); + WRITE_U32(p_ctx, 0, "modification_time"); + WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale"); + WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration"); + } + + WRITE_U16(p_ctx, 0x55c4, "language"); /* ISO-639-2/T language code */ + WRITE_U16(p_ctx, 0, "pre_defined"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_hdlr( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + uint32_t i, handler_size, fourcc = 0; + const char *handler_name; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) fourcc = VC_FOURCC('v','i','d','e'); + if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) fourcc = VC_FOURCC('s','o','u','n'); + if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) fourcc = VC_FOURCC('t','e','x','t'); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + if(module->brand == MP4_BRAND_QT) + WRITE_FOURCC(p_ctx, VC_FOURCC('m','h','l','r'), "component_type"); + else + WRITE_U32(p_ctx, 0, "pre-defined"); + + WRITE_FOURCC(p_ctx, fourcc, "handler_type"); + for(i = 0; i < 3; i++) + WRITE_U32(p_ctx, 0, "reserved"); + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { handler_name = "Video Media Handler"; handler_size = sizeof("Video Media Handler"); } + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { handler_name = "Audio Media Handler"; handler_size = sizeof("Audio Media Handler"); } + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) + { handler_name = "Text Media Handler"; handler_size = sizeof("Text Media Handler"); } + else { handler_name = ""; handler_size = sizeof(""); } + + if(module->brand == MP4_BRAND_QT) + { handler_size--; WRITE_U8(p_ctx, handler_size, "string_size"); } + + WRITE_STRING(p_ctx, handler_name, handler_size, "name"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_minf( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_VMHD); + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_SMHD); +#if 0 + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) + /*FIXME */; +#endif + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_DINF); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STBL); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_vmhd( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 1, "flags"); + + WRITE_U16(p_ctx, 0, "graphicsmode"); + WRITE_U16(p_ctx, 0, "opcolor"); + WRITE_U16(p_ctx, 0, "opcolor"); + WRITE_U16(p_ctx, 0, "opcolor"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_smhd( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + WRITE_U16(p_ctx, 0, "balance"); + WRITE_U16(p_ctx, 0, "reserved"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_dinf( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_DREF); + if(status != VC_CONTAINER_SUCCESS) return status; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_dref( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + WRITE_U32(p_ctx, 1, "entry_count"); + + /* Add a URL box */ + WRITE_U32(p_ctx, 12, "box_size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('u','r','l',' '), "box_type"); + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0x1, "flags"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stbl( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSD); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STTS); + if(status != VC_CONTAINER_SUCCESS) return status; + + if( 0 && track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_CTTS); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSC); + if(status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSZ); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(1) + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STCO); + else + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_CO64); + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSS); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stsd( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + WRITE_U32(p_ctx, 1, "entry_count"); + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + status = mp4_write_box_extended(p_ctx, MP4_BOX_TYPE_VIDE, track->priv->module->fourcc); + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + status = mp4_write_box_extended(p_ctx, MP4_BOX_TYPE_SOUN, track->priv->module->fourcc); +#if 0 + else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) + /*FIXME*/; +#endif + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_write_sample_to_temp( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + int32_t dts_diff = packet->dts - module->prev_sample_dts; + uint8_t keyframe = (packet->flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? 0x80 : 0; + + vc_container_io_write_be_uint32(module->temp.io, packet->size); + vc_container_io_write_be_uint32(module->temp.io, dts_diff); + vc_container_io_write_be_uint24(module->temp.io, (uint32_t)(packet->pts - packet->dts)); + vc_container_io_write_uint8(module->temp.io, packet->track | keyframe); + module->prev_sample_dts = packet->dts; + return module->temp.io->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_read_sample_from_temp( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + packet->size = vc_container_io_read_be_uint32(module->temp.io); + packet->dts += (int32_t)vc_container_io_read_be_uint32(module->temp.io); + packet->pts = packet->dts + vc_container_io_read_be_uint24(module->temp.io); + packet->track = vc_container_io_read_uint8(module->temp.io); + packet->flags = (packet->track & 0x80) ? VC_CONTAINER_PACKET_FLAG_KEYFRAME : 0; + packet->track &= 0x7F; + return module->temp.io->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stts( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T sample; + unsigned int entries = 0; + int64_t last_dts = 0, delta; + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries, "entry_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries * 8); + return STREAM_STATUS(p_ctx); + } + + /* Go through all the samples written */ + vc_container_io_seek(module->temp.io, INT64_C(0)); + sample.dts = 0; + + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + while(status == VC_CONTAINER_SUCCESS) + { + if(sample.track != module->current_track) goto skip; + + delta = sample.dts * MP4_TIMESCALE / 1000000 - last_dts; + if(delta < 0) delta = 0; + WRITE_U32(p_ctx, 1, "sample_count"); + WRITE_U32(p_ctx, delta, "sample_delta"); + entries++; + last_dts += delta; + + skip: + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + } + vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_ctts( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries, "entry_count"); + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stsc( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T sample; + int64_t offset = 0, track_offset = -1; + unsigned int entries = 0, chunks = 0, first_chunk = 0, samples_in_chunk = 0; + + memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries, "entry_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries * 12); + return STREAM_STATUS(p_ctx); + } + + /* Go through all the samples written */ + vc_container_io_seek(module->temp.io, INT64_C(0)); + + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + while(status == VC_CONTAINER_SUCCESS) + { + if(sample.track != module->current_track) goto skip; + + /* Is it a new chunk ? */ + if(track_offset != offset) + { + chunks++; + if(samples_in_chunk) + { + WRITE_U32(p_ctx, first_chunk, "first_chunk"); + WRITE_U32(p_ctx, samples_in_chunk, "samples_per_chunk"); + WRITE_U32(p_ctx, 1, "sample_description_index"); + entries++; + } + first_chunk = chunks; + samples_in_chunk = 0; + } + track_offset = offset + sample.size; + samples_in_chunk++; + + skip: + offset += sample.size; + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + } + + if(samples_in_chunk) + { + WRITE_U32(p_ctx, first_chunk, "first_chunk"); + WRITE_U32(p_ctx, samples_in_chunk, "samples_per_chunk"); + WRITE_U32(p_ctx, 1, "sample_description_index"); + entries++; + } + + vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stsz( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T sample; + unsigned int entries = 0; + + memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + WRITE_U32(p_ctx, 0, "sample_size"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries, "sample_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries * 4); + return STREAM_STATUS(p_ctx); + } + + /* Go through all the samples written */ + vc_container_io_seek(module->temp.io, INT64_C(0)); + + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + while(status == VC_CONTAINER_SUCCESS) + { + if(sample.track != module->current_track) goto skip; + + WRITE_U32(p_ctx, sample.size, "entry_size"); + entries++; + + skip: + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + } + vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stco( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T sample; + int64_t offset = module->data_offset, track_offset = -1; + unsigned int entries = 0; + + memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries, "entry_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries * 4); + return STREAM_STATUS(p_ctx); + } + + /* Go through all the samples written */ + vc_container_io_seek(module->temp.io, INT64_C(0)); + + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + while(status == VC_CONTAINER_SUCCESS) + { + if(sample.track != module->current_track) goto skip; + + /* Is it a new chunk ? */ + if(track_offset != offset) + { + WRITE_U32(p_ctx, offset, "chunk_offset"); + entries++; + } + track_offset = offset + sample.size; + + skip: + offset += sample.size; + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + } + vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_co64( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries, "entry_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries * 8); + return STREAM_STATUS(p_ctx); + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_stss( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T sample; + unsigned int entries = 0, samples = 0; + + memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T)); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries, "entry_count"); + + if(module->null.refcount) + { + /* We're not actually writing the data, we just want the size */ + WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries * 4); + return STREAM_STATUS(p_ctx); + } + + /* Go through all the samples written */ + vc_container_io_seek(module->temp.io, INT64_C(0)); + + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + while(status == VC_CONTAINER_SUCCESS) + { + if(sample.track != module->current_track) goto skip; + + samples++; + if(sample.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) + { + WRITE_U32(p_ctx, samples, "sample_number"); + entries++; + } + + skip: + status = mp4_writer_read_sample_from_temp(p_ctx, &sample); + } + vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_vide_avcC( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + + WRITE_U32(p_ctx, track->format->extradata_size + 8, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('a','v','c','C'), "type"); + WRITE_BYTES(p_ctx, track->format->extradata, track->format->extradata_size); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_vide_d263( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U32(p_ctx, 8 + 7, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('d','2','6','3'), "type"); + WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); + WRITE_U8(p_ctx, 0, "version"); + WRITE_U8(p_ctx, 10, "level"); + WRITE_U8(p_ctx, 0, "profile"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_vide( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int i; + + for(i = 0; i < 6; i++) WRITE_U8(p_ctx, 0, "reserved"); + WRITE_U16(p_ctx, 1, "data_reference_index"); + + WRITE_U16(p_ctx, 0, "pre_defined"); + WRITE_U16(p_ctx, 0, "reserved"); + for(i = 0; i < 3; i++) WRITE_U32(p_ctx, 0, "pre_defined"); + WRITE_U16(p_ctx, track->format->type->video.width, "width"); + WRITE_U16(p_ctx, track->format->type->video.height, "height"); + WRITE_U32(p_ctx, 0x480000, "horizresolution"); /* 72 dpi */ + WRITE_U32(p_ctx, 0x480000, "vertresolution"); /* 72 dpi */ + WRITE_U32(p_ctx, 0, "reserved"); + WRITE_U16(p_ctx, 1, "frame_count"); + for(i = 0; i < 32; i++) _WRITE_U8(p_ctx, 0); + WRITE_U16(p_ctx, 0x18, "depth"); + WRITE_U16(p_ctx, -1, "pre_defined"); + + switch(track->format->codec) + { + case VC_CONTAINER_CODEC_H264: return mp4_write_box_vide_avcC(p_ctx); + case VC_CONTAINER_CODEC_H263: return mp4_write_box_vide_d263(p_ctx); + case VC_CONTAINER_CODEC_MP4V: return mp4_write_box(p_ctx, MP4_BOX_TYPE_ESDS); + default: break; + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_soun_damr( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U32(p_ctx, 8 + 8, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('d','a','m','r'), "type"); + WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); + WRITE_U8(p_ctx, 0, "version"); + WRITE_U8(p_ctx, 0x80, "mode_set"); + WRITE_U8(p_ctx, 0, "mode_change_period"); + WRITE_U8(p_ctx, 1, "frame_per_second"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_soun_dawp( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U32(p_ctx, 8 + 5, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('d','a','w','p'), "type"); + WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); + WRITE_U8(p_ctx, 0, "version"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_soun_devc( VC_CONTAINER_T *p_ctx ) +{ + WRITE_U32(p_ctx, 8 + 6, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('d','e','v','c'), "type"); + WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor"); + WRITE_U8(p_ctx, 0, "version"); + WRITE_U8(p_ctx, 1, "samples_per_frame"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_soun( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int i, version = 0; + + for(i = 0; i < 6; i++) WRITE_U8(p_ctx, 0, "reserved"); + WRITE_U16(p_ctx, 1, "data_reference_index"); + + if(module->brand == MP4_BRAND_QT) + { + if(track->format->codec == VC_CONTAINER_CODEC_MP4A) version = 1; + WRITE_U16(p_ctx, version, "version"); + WRITE_U16(p_ctx, 0, "revision_level"); + WRITE_U32(p_ctx, 0, "vendor"); + } + else + { + for(i = 0; i < 2; i++) WRITE_U32(p_ctx, 0, "reserved"); + } + + WRITE_U16(p_ctx, track->format->type->audio.channels, "channelcount"); + WRITE_U16(p_ctx, 0, "samplesize"); + WRITE_U16(p_ctx, 0, "pre_defined"); + WRITE_U16(p_ctx, 0, "reserved"); + WRITE_U32(p_ctx, track->format->type->audio.sample_rate << 16, "samplerate"); + + if(module->brand == MP4_BRAND_QT && version == 1) /* FIXME */ + { + WRITE_U32(p_ctx, 1024, "samples_per_packet"); + WRITE_U32(p_ctx, 1536, "bytes_per_packet"); + WRITE_U32(p_ctx, 2, "bytes_per_frame"); + WRITE_U32(p_ctx, 2, "bytes_per_sample"); + } + + switch(track->format->codec) + { + case VC_CONTAINER_CODEC_AMRNB: + case VC_CONTAINER_CODEC_AMRWB: + return mp4_write_box_soun_damr(p_ctx); + case VC_CONTAINER_CODEC_AMRWBP: + return mp4_write_box_soun_dawp(p_ctx); + case VC_CONTAINER_CODEC_EVRC: + return mp4_write_box_soun_devc(p_ctx); + case VC_CONTAINER_CODEC_MP4A: + case VC_CONTAINER_CODEC_MPGA: + return mp4_write_box(p_ctx, MP4_BOX_TYPE_ESDS); + default: break; + } + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_write_box_esds( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track]; + unsigned int decoder_specific_size = 0, decoder_config_size, sl_size; + unsigned int stream_type, object_type; + +#define MP4_GET_DESCRIPTOR_SIZE(size) \ + ((size) < 0x0080) ? 2 + (size) : ((size) < 0x4000) ? 3 + (size) : 4 + (size) +#define MP4_WRITE_DESCRIPTOR_HEADER(type, size) \ + LOG_FORMAT(p_ctx, "descriptor %x, size %i", type, size); _WRITE_U8(p_ctx, type); \ + if((size) >= 0x4000) _WRITE_U8(p_ctx, (((size) >> 14) & 0x7F) | 0x80); \ + if((size) >= 0x80 ) _WRITE_U8(p_ctx, (((size) >> 7 ) & 0x7F) | 0x80); \ + _WRITE_U8(p_ctx, (size) & 0x7F) + + /* We only support small size descriptors */ + if(track->format->extradata_size > 0x200000 - 100) + return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; + + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_VIDEO: stream_type = 0x4; break; + case VC_CONTAINER_ES_TYPE_AUDIO: stream_type = 0x5; break; + case VC_CONTAINER_ES_TYPE_SUBPICTURE: stream_type = 0x20; break; + default: return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; + } + switch(track->format->codec) + { + case VC_CONTAINER_CODEC_MP4V: object_type = 0x20; break; + case VC_CONTAINER_CODEC_MP1V: object_type = 0x6B; break; + case VC_CONTAINER_CODEC_MP2V: object_type = 0x60; break; + case VC_CONTAINER_CODEC_JPEG: object_type = 0x6C; break; + case VC_CONTAINER_CODEC_MP4A: object_type = 0x40; break; + case VC_CONTAINER_CODEC_MPGA: + object_type = track->format->type->audio.sample_rate < 32000 ? 0x69 : 0x6B; break; + default: return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; + } + + decoder_specific_size = MP4_GET_DESCRIPTOR_SIZE(track->format->extradata_size); + decoder_config_size = MP4_GET_DESCRIPTOR_SIZE(13 + decoder_specific_size); + sl_size = MP4_GET_DESCRIPTOR_SIZE(1); + + WRITE_U8(p_ctx, 0, "version"); + WRITE_U24(p_ctx, 0, "flags"); + + /* Write the ES descriptor */ + MP4_WRITE_DESCRIPTOR_HEADER(0x3, 3 + decoder_config_size + sl_size); + WRITE_U16(p_ctx, module->current_track + 1, "es_id"); + WRITE_U8(p_ctx, 0x1f, "flags"); /* stream_priority = 0x1f */ + + /* Write the Decoder Config descriptor */ + MP4_WRITE_DESCRIPTOR_HEADER(0x4, 13 + decoder_specific_size); + WRITE_U8(p_ctx, object_type, "object_type_indication"); + WRITE_U8(p_ctx, (stream_type << 2) | 1, "stream_type"); + WRITE_U24(p_ctx, 8000, "buffer_size_db"); + WRITE_U32(p_ctx, track->format->bitrate, "max_bitrate"); + WRITE_U32(p_ctx, track->format->bitrate, "avg_bitrate"); + if(track->format->extradata_size) + { + MP4_WRITE_DESCRIPTOR_HEADER(0x5, track->format->extradata_size); + WRITE_BYTES(p_ctx, track->format->extradata, track->format->extradata_size); + } + + /* Write the SL descriptor */ + MP4_WRITE_DESCRIPTOR_HEADER(0x6, 1); + WRITE_U8(p_ctx, 0x2, "flags"); + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + int64_t mdat_size; + + mdat_size = STREAM_POSITION(p_ctx) - module->mdat_offset; + + /* Write the moov box */ + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MOOV); + + /* Finalise the mdat box */ + SEEK(p_ctx, module->mdat_offset); + WRITE_U32(p_ctx, (uint32_t)mdat_size, "mdat size" ); + + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + + vc_container_writer_extraio_delete(p_ctx, &module->temp); + vc_container_writer_extraio_delete(p_ctx, &module->null); + free(module); + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format ) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_TRACK_T *track; + uint32_t type = 0; + + if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* Check we support this format */ + switch(format->codec) + { + case VC_CONTAINER_CODEC_AMRNB: type = VC_FOURCC('s','a','m','r'); break; + case VC_CONTAINER_CODEC_AMRWB: type = VC_FOURCC('s','a','w','b'); break; + case VC_CONTAINER_CODEC_AMRWBP: type = VC_FOURCC('s','a','w','p'); break; + case VC_CONTAINER_CODEC_EVRC: type = VC_FOURCC('s','e','v','c'); break; + case VC_CONTAINER_CODEC_MP4A: type = VC_FOURCC('m','p','4','a'); break; + case VC_CONTAINER_CODEC_MPGA: type = VC_FOURCC('m','p','4','a'); break; + + case VC_CONTAINER_CODEC_MP4V: type = VC_FOURCC('m','p','4','v'); break; + case VC_CONTAINER_CODEC_JPEG: type = VC_FOURCC('m','p','4','v'); break; + case VC_CONTAINER_CODEC_H263: type = VC_FOURCC('s','2','6','3'); break; + case VC_CONTAINER_CODEC_H264: + if(format->codec_variant == VC_FOURCC('a','v','c','C')) type = VC_FOURCC('a','v','c','1'); break; + case VC_CONTAINER_CODEC_MJPEG: type = VC_FOURCC('j','p','e','g'); break; + case VC_CONTAINER_CODEC_MJPEGA: type = VC_FOURCC('m','j','p','a'); break; + case VC_CONTAINER_CODEC_MJPEGB: type = VC_FOURCC('m','j','p','b'); break; + case VC_CONTAINER_CODEC_MP1V: type = VC_FOURCC('m','p','e','g'); break; + case VC_CONTAINER_CODEC_MP2V: type = VC_FOURCC('m','p','e','g'); break; + + default: type = 0; break; + } + + if(!type) return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; + + /* Allocate and initialise track data */ + if(p_ctx->tracks_num >= MP4_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + p_ctx->tracks[p_ctx->tracks_num] = track = + vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module)); + if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + if(format->extradata_size) + { + status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size ); + if(status) goto error; + } + + vc_container_format_copy(track->format, format, format->extradata_size); + track->priv->module->fourcc = type; + track->priv->module->offset = -1; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size = 8; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size = 12; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size = 4; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_CO64].entry_size = 8; + track->priv->module->sample_table[MP4_SAMPLE_TABLE_CTTS].entry_size = 8; + + p_ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + error: + vc_container_free_track(p_ctx, track); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_add_track_done( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + if(module->tracks_add_done) return status; + + /* We need to find out the size of the object we're going to write it. */ + if(!vc_container_writer_extraio_enable(p_ctx, &module->null)) + { + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MOOV); + module->moov_size = STREAM_POSITION(p_ctx); + p_ctx->size = module->moov_size; + } + vc_container_writer_extraio_disable(p_ctx, &module->null); + + if(status == VC_CONTAINER_SUCCESS) module->tracks_add_done = true; + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + switch(operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + { + VC_CONTAINER_ES_FORMAT_T *p_format = + (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * ); + if(module->tracks_add_done) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + return mp4_writer_add_track(p_ctx, p_format); + } + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + return mp4_writer_add_track_done(p_ctx); + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_add_sample( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[packet->track]; + VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module; + + track_module->last_pts = packet->pts; + if(!track_module->samples) track_module->first_pts = packet->pts; + + track_module->samples++; + track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries++; /* sample size */ + p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size; + track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries++; /* time to sample */ + p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size; + + #if 0 + delta_ts = packet->dts - track_module->timestamp; + track_module->timestamp = packet->dts; + if(!track_module->samples) track_module->delta_ts = + if() +#endif + + /* Is it a new chunk ? */ + if(module->sample_offset != track_module->offset) + { + track_module->chunks++; + track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries++; /* chunk offset */ + p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size; + track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries++; /* sample to chunk */ + p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size; + } + track_module->offset = module->sample_offset + packet->size; + + if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && + (packet->flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME)) + { + track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries++; /* sync sample */ + p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size; + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mp4_writer_write( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PACKET_T *sample = &module->sample; + VC_CONTAINER_STATUS_T status; + + if(!module->tracks_add_done) + { + status = mp4_writer_add_track_done(p_ctx); + if(status != VC_CONTAINER_SUCCESS) return status; + } + + if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START) + ++module->samples; /* Switching to a new sample */ + + if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START) + { + module->sample_offset = STREAM_POSITION(p_ctx); + sample->size = packet->size; + sample->pts = packet->pts; + sample->dts = packet->pts; + sample->track = packet->track; + sample->flags = packet->flags; + } + else + { + sample->size += packet->size; + sample->flags |= packet->flags; + } + + if(WRITE_BYTES(p_ctx, packet->data, packet->size) != packet->size) + return STREAM_STATUS(p_ctx); // TODO do something + p_ctx->size += packet->size; + + // + if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) + { + status = mp4_writer_write_sample_to_temp(p_ctx, sample); + status = mp4_writer_add_sample(p_ctx, sample); + } + + return VC_CONTAINER_SUCCESS; +} + +/****************************************************************************** +Global function definitions. +******************************************************************************/ + +VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + MP4_BRAND_T brand; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "3gp") && strcasecmp(extension, "skm") && + strcasecmp(extension, "mov") && strcasecmp(extension, "mp4") && + strcasecmp(extension, "m4v") && strcasecmp(extension, "m4a")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + + /* Find out which brand we're going write */ + if(!strcasecmp(extension, "3gp")) brand = MP4_BRAND_3GP5; + else if(!strcasecmp(extension, "skm")) brand = MP4_BRAND_SKM2; + else if(!strcasecmp(extension, "mov")) brand = MP4_BRAND_QT; + else brand = MP4_BRAND_ISOM; + module->brand = brand; + + /* Create a null i/o writer to help us out in writing our data */ + status = vc_container_writer_extraio_create_null(p_ctx, &module->null); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* Create a temporary i/o writer to help us out in writing our data */ + status = vc_container_writer_extraio_create_temp(p_ctx, &module->temp); + if(status != VC_CONTAINER_SUCCESS) goto error; + + status = mp4_write_box(p_ctx, MP4_BOX_TYPE_FTYP); + if(status != VC_CONTAINER_SUCCESS) goto error; + + /* Start the mdat box */ + module->mdat_offset = STREAM_POSITION(p_ctx); + WRITE_U32(p_ctx, 0, "size"); + WRITE_FOURCC(p_ctx, VC_FOURCC('m','d','a','t'), "type"); + module->data_offset = STREAM_POSITION(p_ctx); + + p_ctx->priv->pf_close = mp4_writer_close; + p_ctx->priv->pf_write = mp4_writer_write; + p_ctx->priv->pf_control = mp4_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "mp4: error opening stream"); + if(module) + { + if(module->null.io) vc_container_writer_extraio_delete(p_ctx, &module->null); + free(module); + } + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open mp4_writer_open +#endif diff --git a/containers/mpeg/CMakeLists.txt b/containers/mpeg/CMakeLists.txt new file mode 100755 index 0000000..5afe585 --- /dev/null +++ b/containers/mpeg/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_ps ${LIBRARY_TYPE} ps_reader.c) + +target_link_libraries(reader_ps containers) + +install(TARGETS reader_ps DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/mpeg/ps_reader.c b/containers/mpeg/ps_reader.c new file mode 100755 index 0000000..1394300 --- /dev/null +++ b/containers/mpeg/ps_reader.c @@ -0,0 +1,1268 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#include "containers/core/containers_bits.h" +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#undef CONTAINER_HELPER_LOG_INDENT +#define CONTAINER_HELPER_LOG_INDENT(a) (2*(a)->priv->module->level) + +/****************************************************************************** +Defines. +******************************************************************************/ +#define PS_TRACKS_MAX 2 +#define PS_EXTRADATA_MAX 256 + +#define PS_SYNC_FAIL_MAX 65536 /** Maximum number of byte-wise sync attempts, + should be enough to stride at least one + PES packet (length encoded using 16 bits). */ + +/** Maximum number of pack/packet start codes scanned when searching for tracks + at open time or when resyncing. */ +#define PS_PACK_SCAN_MAX 128 + +/****************************************************************************** +Type definitions. +******************************************************************************/ +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + /** Coding and elementary stream id of the track */ + uint32_t stream_id; + + /** Sub-stream id (for private_stream_1 only) */ + uint32_t substream_id; + + /** PES packet payload offset (for private_stream_1) */ + unsigned int payload_offset; + + uint8_t extradata[PS_EXTRADATA_MAX]; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + /** Logging indentation level */ + uint32_t level; + + /** Track data */ + int tracks_num; + VC_CONTAINER_TRACK_T *tracks[PS_TRACKS_MAX]; + + /** State flag denoting whether or not we are searching + for tracks (at open time) */ + bool searching_tracks; + + /** Size of program stream data (if known) */ + uint64_t data_size; + + /** Offset to the first pack or PES packet start code we've seen */ + uint64_t data_offset; + + /** The first system_clock_reference value we've seen, in (27MHz ticks) */ + int64_t scr_offset; + + /** Most recent system_clock_reference value we've seen, in (27MHz ticks) */ + int64_t scr; + + /** Global offset we add to PES timestamps to make them zero based and + to work around discontinuity in the system_clock_reference */ + int64_t scr_bias; + + /** Most recent program stream mux rate (in units of 50 bytes/second). */ + uint32_t mux_rate; + + /** Offset to the most recent pack start code we've seen */ + uint64_t pack_offset; + + /** Program stream mux rate is often incorrect or fixed to 25200 (10.08 + Mbit/s) which yields inaccurate duration estimate for most files. We + maintain a moving average data rate (in units of bytes/second) based + on the system_clock_reference to give better estimates. */ + int64_t data_rate; + + /** Offset to the most recent PES packet start code prefix we've seen */ + unsigned int packet_data_size; + unsigned int packet_data_left; + int64_t packet_pts; + int64_t packet_dts; + int packet_track; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ + +VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Prototypes for local functions +******************************************************************************/ + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/** Find the track associated with a PS stream_id */ +static VC_CONTAINER_TRACK_T *ps_find_track( VC_CONTAINER_T *ctx, uint32_t stream_id, + uint32_t substream_id, bool b_create ) +{ + VC_CONTAINER_TRACK_T *track = 0; + unsigned int i; + + for(i = 0; i < ctx->tracks_num; i++) + if(ctx->tracks[i]->priv->module->stream_id == stream_id && + ctx->tracks[i]->priv->module->substream_id == substream_id) break; + + if(i < ctx->tracks_num) /* We found it */ + track = ctx->tracks[i]; + + if(!track && b_create && i < PS_TRACKS_MAX) + { + /* Allocate and initialise a new track */ + ctx->tracks[i] = track = + vc_container_allocate_track(ctx, sizeof(*ctx->tracks[0]->priv->module)); + if(track) + { + track->priv->module->stream_id = stream_id; + track->priv->module->substream_id = substream_id; + ctx->tracks_num++; + } + } + + if(!track && b_create) + LOG_DEBUG(ctx, "could not create track for stream id: %i", stream_id); + + return track; +} + +/*****************************************************************************/ +STATIC_INLINE VC_CONTAINER_STATUS_T ps_find_start_code( VC_CONTAINER_T *ctx, uint8_t *buffer ) +{ + unsigned int i; + + /* Scan for a pack or PES packet start code prefix */ + for (i = 0; i < PS_SYNC_FAIL_MAX; ++i) + { + if(PEEK_BYTES(ctx, buffer, 4) < 4) + return VC_CONTAINER_ERROR_EOS; + + if(buffer[0] == 0x0 && buffer[1] == 0x0 && buffer[2] == 0x1 && buffer[3] >= 0xB9) + break; + + if (SKIP_BYTES(ctx, 1) != 1) + return VC_CONTAINER_ERROR_EOS; + } + + if(i == PS_SYNC_FAIL_MAX) /* We didn't find a valid pack or PES packet */ + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if (buffer[3] == 0xB9) /* MPEG_program_end_code */ + return VC_CONTAINER_ERROR_EOS; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_system_header( VC_CONTAINER_T *ctx ) +{ + uint8_t header[8]; + uint32_t length; + VC_CONTAINER_BITS_T bits; + + if(_READ_U32(ctx) != 0x1BB) return VC_CONTAINER_ERROR_CORRUPTED; + LOG_FORMAT(ctx, "system_header"); + ctx->priv->module->level++; + + length = READ_U16(ctx, "header_length"); + if(length < 6) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS; + + BITS_INIT(ctx, &bits, header, 6); + + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 22, "rate_bound"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 6, "audio_bound"); + BITS_SKIP(ctx, &bits, 1, "fixed_flag"); + BITS_SKIP(ctx, &bits, 1, "CSPS_flag"); + BITS_SKIP(ctx, &bits, 1, "system_audio_lock_flag"); + BITS_SKIP(ctx, &bits, 1, "system_video_lock_flag"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 5, "video_bound"); + BITS_SKIP(ctx, &bits, 1, "packet_rate_restriction_flag"); + BITS_SKIP(ctx, &bits, 7, "reserved_bits"); + length -= 6; + + while(length >= 3 && (PEEK_U8(ctx) & 0x80)) + { + SKIP_U8(ctx, "stream_id"); + SKIP_BYTES(ctx, 2); + length -= 3; + } + SKIP_BYTES(ctx, length); + + ctx->priv->module->level--; + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_pack_header( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + uint8_t header[10]; + int64_t scr, scr_base, scr_ext = INT64_C(0); + uint64_t pack_offset = STREAM_POSITION(ctx); + uint32_t mux_rate, stuffing; + VC_CONTAINER_BITS_T bits; + VC_CONTAINER_STATUS_T status; + + if(_READ_U32(ctx) != 0x1BA) return VC_CONTAINER_ERROR_CORRUPTED; + LOG_FORMAT(ctx, "pack_header"); + + module->level++; + + if (PEEK_U8(ctx) & 0x40) /* program stream */ + { + if(READ_BYTES(ctx, header, 10) != 10) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 10); + if(BITS_READ_U32(ctx, &bits, 2, "'01' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]"); + LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_ext = BITS_READ_U32(ctx, &bits, 9, "system_clock_reference_extension"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 5, "reserved"); + stuffing = BITS_READ_U32(ctx, &bits, 3, "pack_stuffing_length"); + SKIP_BYTES(ctx, stuffing); + } + else /* system stream */ + { + if(READ_BYTES(ctx, header, 8) != 8) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 8); + if(BITS_READ_U32(ctx, &bits, 4, "'0010' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]"); + LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + } + + if ((status = STREAM_STATUS(ctx)) != VC_CONTAINER_SUCCESS) return status; + + module->level--; + + /* Set or update system_clock_reference, adjust bias if necessary */ + scr = scr_base * INT64_C(300) + scr_ext; + + if (module->scr_offset == VC_CONTAINER_TIME_UNKNOWN) + module->scr_offset = scr; + + if (module->scr == VC_CONTAINER_TIME_UNKNOWN) + module->scr_bias = -scr; + else if (scr < module->scr) + module->scr_bias = module->scr - scr; + + if (module->scr != VC_CONTAINER_TIME_UNKNOWN) + { + /* system_clock_reference is not necessarily continuous across the entire stream */ + if (scr > module->scr) + { + int64_t data_rate; + data_rate = INT64_C(27000000) * (pack_offset - module->pack_offset) / (scr - module->scr); + + if (module->data_rate) + { + /* Simple moving average over data rate seen so far */ + module->data_rate = (module->data_rate * 31 + data_rate) >> 5; + } + else + { + module->data_rate = mux_rate * 50; + } + } + + module->pack_offset = pack_offset; + } + + module->scr = scr; + module->mux_rate = mux_rate; + + /* Check for a system header */ + if(PEEK_U32(ctx) == 0x1BB) + return ps_read_system_header(ctx); + + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static void ps_get_stream_coding( VC_CONTAINER_T *ctx, unsigned int stream_id, + VC_CONTAINER_ES_TYPE_T *p_type, VC_CONTAINER_FOURCC_T *p_codec, + VC_CONTAINER_FOURCC_T *p_variant) +{ + VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN; + VC_CONTAINER_FOURCC_T codec = VC_CONTAINER_CODEC_UNKNOWN; + VC_CONTAINER_FOURCC_T variant = 0; + + VC_CONTAINER_PARAM_UNUSED(ctx); + + if (stream_id == 0xE2) /* FIXME: why is this stream number reserved for H264? */ + { + type = VC_CONTAINER_ES_TYPE_VIDEO; + codec = VC_CONTAINER_CODEC_H264; + } + else if ((stream_id & 0xF0) == 0xE0) + { + type = VC_CONTAINER_ES_TYPE_VIDEO; + codec = VC_CONTAINER_CODEC_MP2V; + } + else if ((stream_id & 0xE0) == 0xC0) + { + type = VC_CONTAINER_ES_TYPE_AUDIO; + codec = VC_CONTAINER_CODEC_MPGA; + variant = VC_CONTAINER_VARIANT_MPGA_L2; + } + + /* FIXME: PRIVATE_EVOB_PES_PACKET with stream_id 0xFD ? */ + + *p_type = type; + *p_codec = codec; + *p_variant = variant; +} + +/*****************************************************************************/ +static int64_t ps_pes_time_to_us( VC_CONTAINER_T *ctx, int64_t time ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + + if (time == VC_CONTAINER_TIME_UNKNOWN) + return VC_CONTAINER_TIME_UNKNOWN; + + /* Need to wait for system_clock_reference first */ + if (module->scr_bias == VC_CONTAINER_TIME_UNKNOWN) + return VC_CONTAINER_TIME_UNKNOWN; + + /* Can't have valid bias without known system_clock_reference */ + vc_container_assert(module->scr != VC_CONTAINER_TIME_UNKNOWN); + + /* 90kHz (PES) clock --> (zero based) 27MHz system clock --> microseconds */ + return (INT64_C(300) * time + module->scr_bias) / INT64_C(27); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_pes_time( VC_CONTAINER_T *ctx, + uint32_t *p_length, unsigned int pts_dts, int64_t *p_pts, int64_t *p_dts ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint8_t header[10]; + uint32_t length = *p_length; + VC_CONTAINER_BITS_T bits; + int64_t pts, dts; + + if (p_pts) *p_pts = VC_CONTAINER_TIME_UNKNOWN; + if (p_dts) *p_dts = VC_CONTAINER_TIME_UNKNOWN; + + if (pts_dts == 0x2) + { + /* PTS only */ + LOG_FORMAT(ctx, "PTS"); + ctx->priv->module->level++; + if(length < 5) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 5) != 5) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 5); + + if(BITS_READ_U32(ctx, &bits, 4, "'0010' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED; + pts = BITS_READ_U32(ctx, &bits, 3, "PTS [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [14..0]"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + LOG_FORMAT(ctx, "PTS %"PRId64, pts); + if (p_pts) *p_pts = pts; + length -= 5; + ctx->priv->module->level--; + } + else if (pts_dts == 0x3) + { + /* PTS & DTS */ + LOG_FORMAT(ctx, "PTS DTS"); + ctx->priv->module->level++; + if(length < 10) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 10) != 10) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 10); + + /* PTS */ + if(BITS_READ_U32(ctx, &bits, 4, "'0011' marker bits") != 0x3) return VC_CONTAINER_ERROR_CORRUPTED; + pts = BITS_READ_U32(ctx, &bits, 3, "PTS [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [14..0]"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + + /* DTS */ + if(BITS_READ_U32(ctx, &bits, 4, "'0001' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + dts = BITS_READ_U32(ctx, &bits, 3, "DTS [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + dts |= BITS_READ_U32(ctx, &bits, 15, "DTS [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + dts |= BITS_READ_U32(ctx, &bits, 15, "DTS [14..0]"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + LOG_FORMAT(ctx, "PTS %"PRId64, pts); + LOG_FORMAT(ctx, "DTS %"PRId64, dts); + if (p_pts) *p_pts = pts; + if (p_dts) *p_dts = dts; + length -= 10; + ctx->priv->module->level--; + } + else + { + status = VC_CONTAINER_ERROR_NOT_FOUND; + } + + *p_length = *p_length - length; + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_pes_extension( VC_CONTAINER_T *ctx, + uint32_t *p_length ) +{ + unsigned int pes_private_data, pack_header, packet_seq_counter, pstd_buffer, extension2; + uint8_t header[2]; + uint32_t length = *p_length; + VC_CONTAINER_BITS_T bits; + unsigned int i; + + LOG_FORMAT(ctx, "PES_extension"); + ctx->priv->module->level++; + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 1); + + pes_private_data = BITS_READ_U32(ctx, &bits, 1, "PES_private_data_flag"); + pack_header = BITS_READ_U32(ctx, &bits, 1, "pack_header_field_flag"); + packet_seq_counter = BITS_READ_U32(ctx, &bits, 1, "program_packet_sequence_counter_flag"); + pstd_buffer = BITS_READ_U32(ctx, &bits, 1, "P-STD_buffer_flag"); + BITS_SKIP(ctx, &bits, 3, "3 reserved_bits"); + extension2 = BITS_READ_U32(ctx, &bits, 1, "PES_extension_flag_2"); + length -= 1; + + if (pes_private_data) + { + if(length < 16) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_BYTES(ctx, 16); /* PES_private_data */ + length -= 16; + } + + if (pack_header) + { + unsigned int pack_field_len; + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + pack_field_len = READ_U8(ctx, "pack_field_length"); + length -= 1; + if(length < pack_field_len) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_BYTES(ctx, pack_field_len); /* pack_header */ + length -= pack_field_len; + } + + if (packet_seq_counter) + { + if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 2) != 2) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 2); + + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 7, "program_packet_sequence_counter"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 1, "MPEG1_MPEG2_identifier"); + BITS_SKIP(ctx, &bits, 6, "original_stuff_length"); + length -= 2; + } + + if (pstd_buffer) + { + if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 2) != 2) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 2); + + if(BITS_READ_U32(ctx, &bits, 2, "'01' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 1, "P-STD_buffer_scale"); + BITS_SKIP(ctx, &bits, 13, "P-STD_buffer_size"); + length -= 2; + } + + if (extension2) + { + uint8_t ext_field_len; + + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, &ext_field_len, 1) != 1) return VC_CONTAINER_ERROR_EOS; + length -= 1; + + if((ext_field_len & 0x80) != 0x80) return VC_CONTAINER_ERROR_CORRUPTED; /* marker_bit */ + ext_field_len &= ~0x80; + LOG_FORMAT(ctx, "PES_extension_field_length %d", ext_field_len); + + for (i = 0; i < ext_field_len; i++) + { + SKIP_U8(ctx, "reserved"); + length--; + } + } + + ctx->priv->module->level--; + + *p_length = *p_length - length; /* Number of bytes read from stream */ + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_pes_packet_header( VC_CONTAINER_T *ctx, + uint32_t *p_length, int64_t *p_pts, int64_t *p_dts ) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_BITS_T bits; + uint32_t size, length = *p_length; + unsigned int pts_dts; + uint8_t header[10]; + + if(length < 3) return VC_CONTAINER_ERROR_CORRUPTED; + + if ((PEEK_U8(ctx) & 0xC0) == 0x80) /* program stream */ + { + unsigned int escr, es_rate, dsm_trick_mode, additional_copy_info, pes_crc, pes_extension; + unsigned int header_length; + + if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 3); + + if (BITS_READ_U32(ctx, &bits, 2, "'10' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 2, "PES_scrambling_control"); + BITS_SKIP(ctx, &bits, 1, "PES_priority"); + BITS_SKIP(ctx, &bits, 1, "data_alignment_indicator"); + BITS_SKIP(ctx, &bits, 1, "copyright"); + BITS_SKIP(ctx, &bits, 1, "original_or_copy"); + pts_dts = BITS_READ_U32(ctx, &bits, 2, "PTS_DTS_flags"); + escr = BITS_READ_U32(ctx, &bits, 1, "ESCR_flag"); + es_rate = BITS_READ_U32(ctx, &bits, 1, "ES_rate_flag"); + dsm_trick_mode = BITS_READ_U32(ctx, &bits, 1, "DSM_trick_mode_flag"); + additional_copy_info = BITS_READ_U32(ctx, &bits, 1, "additional_copy_info_flag"); + pes_crc = BITS_READ_U32(ctx, &bits, 1, "PES_CRC_flag"); + pes_extension = BITS_READ_U32(ctx, &bits, 1, "PES_extension_flag"); + header_length = BITS_READ_U32(ctx, &bits, 8, "PES_header_data_length"); + length -= 3; + + size = length; + status = ps_read_pes_time(ctx, &size, pts_dts, p_pts, p_dts); + if (status && status != VC_CONTAINER_ERROR_NOT_FOUND) return status; + length -= size; + header_length -= size; + + if (escr) + { + /* Elementary stream clock reference */ + int64_t escr; + + ctx->priv->module->level++; + if(length < 6) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 6); + + BITS_SKIP(ctx, &bits, 2, "reserved_bits"); + escr = BITS_READ_U32(ctx, &bits, 3, "ESCR_base [32..30]") << 30; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + escr |= BITS_READ_U32(ctx, &bits, 15, "ESCR_base [29..15]") << 15; + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + escr |= BITS_READ_U32(ctx, &bits, 15, "ESCR_base [14..0]"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_READ_U32(ctx, &bits, 9, "ESCR_extension"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + + LOG_FORMAT(ctx, "ESCR_base %"PRId64, escr); + length -= 6; + header_length -= 6; + ctx->priv->module->level--; + } + + if (es_rate) + { + /* Elementary stream rate */ + if(length < 3) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 3); + + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_READ_U32(ctx, &bits, 22, "ES_rate"); + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + length -= 3; + header_length -= 3; + } + + if (dsm_trick_mode) + { + unsigned int trick_mode; + + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 1); + + trick_mode = BITS_READ_U32(ctx, &bits, 3, "trick_mode_control"); + if (trick_mode == 0x0 /* fast_forward */) + { + BITS_SKIP(ctx, &bits, 2, "field_id"); + BITS_SKIP(ctx, &bits, 1, "intra_slice_refresh"); + BITS_SKIP(ctx, &bits, 2, "frequency_truncation"); + } + else if (trick_mode == 0x1 /* slow_motion */) + { + BITS_SKIP(ctx, &bits, 5, "rep_cntrl"); + } + else if (trick_mode == 0x2 /* freeze_frame */) + { + BITS_SKIP(ctx, &bits, 2, "field_id"); + BITS_SKIP(ctx, &bits, 3, "reserved_bits"); + } + else if (trick_mode == 0x3 /* fast_reverse */) + { + BITS_SKIP(ctx, &bits, 2, "field_id"); + BITS_SKIP(ctx, &bits, 1, "intra_slice_refresh"); + BITS_SKIP(ctx, &bits, 2, "frequency_truncation"); + } + else if (trick_mode == 0x4 /* slow_reverse */) + BITS_SKIP(ctx, &bits, 5, "rep_cntrl"); + else + BITS_SKIP(ctx, &bits, 5, "5 reserved_bits"); + + length -= 1; + header_length -= 1; + } + + if (additional_copy_info) + { + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 1); + + if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + BITS_SKIP(ctx, &bits, 7, "additional_copy_info"); + + length -= 1; + header_length -= 1; + } + + if (pes_crc) + { + SKIP_U16(ctx, "previous_PES_packet_CRC"); + length -= 2; + header_length -= 2; + } + + if (pes_extension) + { + size = length; + if ((status = ps_read_pes_extension(ctx, &size)) != VC_CONTAINER_SUCCESS) return status; + length -= size; + header_length -= size; + } + + if (header_length <= length) + { + SKIP_BYTES(ctx, header_length); /* header stuffing */ + length -= header_length; + } + } + else /* MPEG 1 PES header */ + { + if(length < 12) return VC_CONTAINER_ERROR_CORRUPTED; + + while (PEEK_U8(ctx) == 0xFF && length > 0) + { + SKIP_U8(ctx, "stuffing"); + length--; + } + + if (length == 0) return VC_CONTAINER_ERROR_CORRUPTED; + + if ((PEEK_U8(ctx) & 0xC0) == 0x40) + { + if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_U8(ctx, "???"); + SKIP_U8(ctx, "???"); + length -= 2; + } + + pts_dts = (PEEK_U8(ctx) & 0x30) >> 4; + size = length; + status = ps_read_pes_time(ctx, &size, pts_dts, p_pts, p_dts); + if (status && status != VC_CONTAINER_ERROR_NOT_FOUND) + return status; + length -= size; + + if (status == VC_CONTAINER_ERROR_NOT_FOUND) + { + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_U8(ctx, "???"); + length -= 1; + } + } + + *p_length = length; + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_private_stream_1_coding( VC_CONTAINER_T *ctx, + VC_CONTAINER_ES_TYPE_T *p_type, VC_CONTAINER_FOURCC_T *p_codec, + uint32_t *substream_id, uint32_t *p_length ) +{ + VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN; + VC_CONTAINER_FOURCC_T codec = VC_CONTAINER_CODEC_UNKNOWN; + uint32_t length; + uint8_t id = 0; + + length = *p_length; + + if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, &id, 1) != 1) return VC_CONTAINER_ERROR_EOS; + length -= 1; + + LOG_FORMAT(ctx, "private_stream_1 byte: 0x%x (%u)", id, id); + + if (id >= 0x20 && id <= 0x3f) + { + type = VC_CONTAINER_ES_TYPE_SUBPICTURE; + codec = VC_CONTAINER_CODEC_UNKNOWN; + } + else if ((id >= 0x80 && id <= 0x87) || (id >= 0xC0 && id <= 0xCF)) + { + type = VC_CONTAINER_ES_TYPE_AUDIO; + codec = VC_CONTAINER_CODEC_AC3; + } + else if ((id >= 0x88 && id <= 0x8F) || (id >= 0x98 && id <= 0x9F)) + { + type = VC_CONTAINER_ES_TYPE_AUDIO; + codec = VC_CONTAINER_CODEC_DTS; + } + else if (id >= 0xA0 && id <= 0xBF) + { + type = VC_CONTAINER_ES_TYPE_AUDIO; + codec = VC_CONTAINER_CODEC_PCM_SIGNED; + } + else + { + LOG_FORMAT(ctx, "Unknown private_stream_1 byte: 0x%x (%u)", id, id); + } + + *substream_id = id; + *p_type = type; + *p_codec = codec; + *p_length = length; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_private_stream_1_format( VC_CONTAINER_T *ctx, + VC_CONTAINER_ES_FORMAT_T *format, uint32_t *length ) +{ + uint8_t header[8]; + VC_CONTAINER_BITS_T bits; + + if (format->codec == VC_CONTAINER_CODEC_PCM_SIGNED) + { + static const unsigned fs_tab[4] = { 48000, 96000, 44100, 32000 }; + static const unsigned bps_tab[] = {16, 20, 24, 0}; + + unsigned fs, bps, nchan; + + if(*length < 6) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS; + BITS_INIT(ctx, &bits, header, 6); + + BITS_SKIP(ctx, &bits, 8, "???"); + BITS_SKIP(ctx, &bits, 8, "???"); + BITS_SKIP(ctx, &bits, 8, "???"); + BITS_SKIP(ctx, &bits, 1, "emphasis"); + BITS_SKIP(ctx, &bits, 1, "mute"); + BITS_SKIP(ctx, &bits, 1, "reserved"); + BITS_SKIP(ctx, &bits, 5, "frame number"); + bps = BITS_READ_U32(ctx, &bits, 2, "quant"); + fs = BITS_READ_U32(ctx, &bits, 2, "freq"); + BITS_SKIP(ctx, &bits, 1, "reserved"); + nchan = BITS_READ_U32(ctx, &bits, 3, "channels"); + *length -= 6; + + format->type->audio.sample_rate = fs_tab[fs]; + format->type->audio.bits_per_sample = bps_tab[bps]; + format->type->audio.channels = nchan + 1; + format->type->audio.block_align = + (format->type->audio.channels * format->type->audio.bits_per_sample + 7 ) / 8; + } + else + { + if(*length < 3) return VC_CONTAINER_ERROR_CORRUPTED; + SKIP_U8(ctx, "num of frames"); + SKIP_U8(ctx, "start pos hi"); + SKIP_U8(ctx, "start pos lo"); + *length -= 3; + } + + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_read_pes_packet( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint8_t header[10]; + VC_CONTAINER_BITS_T bits; + uint32_t length, stream_id, substream_id = 0; + VC_CONTAINER_ES_TYPE_T type; + VC_CONTAINER_FOURCC_T codec, variant = 0; + VC_CONTAINER_TRACK_T *track; + int64_t pts, dts; + + if(_READ_U24(ctx) != 0x1) return VC_CONTAINER_ERROR_CORRUPTED; + if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS; + LOG_FORMAT(ctx, "pes_packet_header"); + module->level++; + + BITS_INIT(ctx, &bits, header, 3); + stream_id = BITS_READ_U32(ctx, &bits, 8, "stream_id"); + length = BITS_READ_U32(ctx, &bits, 16, "PES_packet_length"); + + if (stream_id < 0xBC) return VC_CONTAINER_ERROR_CORRUPTED; + + if (stream_id == 0xBC /* program_stream_map */ || stream_id == 0xBE /* padding_stream */ || + stream_id == 0xBF /* private_stream_2 */ || stream_id == 0xF0 /* ECM */ || + stream_id == 0xF1 /* EMM */ || stream_id == 0xFF /* program_stream_directory */ || + stream_id == 0xF2 /* DSMCC_stream */ || stream_id == 0xF8 /* ITU-T Rec. H.222.1 type E */) + goto skip; + + /* Parse PES packet header */ + if ((status = ps_read_pes_packet_header(ctx, &length, &pts, &dts)) != VC_CONTAINER_SUCCESS) + return status; + + /* For private_stream_1, encoding format is stored in the payload */ + if (stream_id == 0xBD) + { + status = ps_read_private_stream_1_coding(ctx, &type, &codec, &substream_id, &length); + if (status) return status; + } + else + ps_get_stream_coding(ctx, stream_id, &type, &codec, &variant); + + /* Check that we know what to do with this track */ + if(type == VC_CONTAINER_ES_TYPE_UNKNOWN || codec == VC_CONTAINER_CODEC_UNKNOWN) + goto skip; + + track = ps_find_track(ctx, stream_id, substream_id, module->searching_tracks); + if(!track) goto skip; + + if (module->searching_tracks) + { + track->is_enabled = true; + track->format->es_type = type; + track->format->codec = codec; + track->format->codec_variant = variant; + + /* For private_stream_1, we need to parse payload further to get elementary stream + format */ + if (stream_id == 0xBD) + { + uint32_t current_length = length; + status = ps_read_private_stream_1_format(ctx, track->format, &length); + if (status) return status; + track->priv->module->payload_offset = current_length - length; + } + + goto skip; + } + else + { + unsigned i; + SKIP_BYTES(ctx, track->priv->module->payload_offset); + length -= track->priv->module->payload_offset; + + /* Find track index */ + for(i = 0; i < ctx->tracks_num; i++) + if(ctx->tracks[i] == track) break; + vc_container_assert(i < ctx->tracks_num); + + module->packet_track = i; + module->packet_data_size = length; + module->packet_pts = pts; + module->packet_dts = dts; + } + +end: + module->level--; + return STREAM_STATUS(ctx); + +skip: + SKIP_BYTES(ctx, length); /* remaining PES_packet_data */ + goto end; +} + +/*****************************************************************************/ +STATIC_INLINE VC_CONTAINER_STATUS_T ps_find_pes_packet( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + uint8_t buffer[4]; + unsigned int i; + + module->packet_data_size = 0; + + for (i = 0; i != PS_PACK_SCAN_MAX; ++i) + { + if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS) + break; + + if (buffer[3] == 0xBA && ((status = ps_read_pack_header(ctx)) != VC_CONTAINER_SUCCESS)) + continue; /* pack start code but parsing failed, goto resync */ + + if ((status = ps_read_pes_packet(ctx)) == VC_CONTAINER_SUCCESS) + break; + } + + return status; +} + +/***************************************************************************** +Functions exported as part of the Container Module API +*****************************************************************************/ + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_reader_read( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + + vc_container_assert(!module->searching_tracks); + + while(!module->packet_data_left) + { + if(ps_find_pes_packet(ctx) != VC_CONTAINER_SUCCESS) + { + status = VC_CONTAINER_ERROR_EOS; + goto error; + } + + module->packet_data_left = module->packet_data_size; + } + + p_packet->track = module->packet_track; + p_packet->size = module->packet_data_left; + p_packet->flags = 0; + p_packet->pts = ps_pes_time_to_us(ctx, module->packet_pts); + p_packet->dts = ps_pes_time_to_us(ctx, module->packet_dts); + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + { + SKIP_BYTES(ctx, module->packet_data_left); + module->packet_data_left = 0; + return VC_CONTAINER_SUCCESS; + } + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + p_packet->size = MIN(p_packet->buffer_size, module->packet_data_left); + p_packet->size = READ_BYTES(ctx, p_packet->data, p_packet->size); + module->packet_data_left -= p_packet->size; + + /* Temporary work-around for lpcm audio */ + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[module->packet_track]; + if (track->format->codec == VC_CONTAINER_CODEC_PCM_SIGNED) + { + unsigned i; + uint16_t *ptr = (uint16_t *)p_packet->data; + + for (i = 0; i < p_packet->size / 2; i ++) + { + uint32_t v = *ptr; + *ptr++ = v >> 8 | ( (v & 0xFF) << 8 ); + } + } + } + + if (module->packet_data_left) + module->packet_pts = module->packet_dts = VC_CONTAINER_TIME_UNKNOWN; + + return STREAM_STATUS(ctx); + +error: + if (status == VC_CONTAINER_ERROR_EOS) + { + /* Reset time reference and calculation state */ + ctx->priv->module->scr = VC_CONTAINER_TIME_UNKNOWN; + ctx->priv->module->scr_bias = -module->scr_offset; + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_reader_seek( VC_CONTAINER_T *ctx, + int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint64_t seekpos, position; + int64_t scr; + + VC_CONTAINER_PARAM_UNUSED(flags); + + if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(ctx)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + position = STREAM_POSITION(ctx); + scr = module->scr; + + if (*p_offset == INT64_C(0)) + seekpos = module->data_offset; + else + { + if (!ctx->duration) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + /* The following is an estimate that might be quite inaccurate */ + seekpos = module->data_offset + (*p_offset * module->data_size) / ctx->duration; + } + + SEEK(ctx, seekpos); + module->scr = module->scr_offset; + status = ps_find_pes_packet(ctx); + if (status && status != VC_CONTAINER_ERROR_EOS) + goto error; + + module->packet_data_left = module->packet_data_size; + + if (module->packet_pts != VC_CONTAINER_TIME_UNKNOWN) + *p_offset = ps_pes_time_to_us(ctx, module->packet_pts); + else if (module->data_size) + *p_offset = (STREAM_POSITION(ctx) - module->data_offset) * ctx->duration / module->data_size; + + return STREAM_STATUS(ctx); + +error: + module->scr = scr; + SEEK(ctx, position); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T ps_reader_close( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int i; + + for(i = 0; i < ctx->tracks_num; i++) + vc_container_free_track(ctx, ctx->tracks[i]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T *ctx ) +{ + const char *extension = vc_uri_path_extension(ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + uint8_t buffer[4]; + unsigned int i; + + /* Check if the user has specified a container */ + vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); + + /* Since MPEG is difficult to auto-detect, we use the extension as + part of the autodetection */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "ps") && strcasecmp(extension, "vob") && + strcasecmp(extension, "mpg") && strcasecmp(extension, "mp2") && + strcasecmp(extension, "mp3") && strcasecmp(extension, "mpeg")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* We didn't find a valid pack or PES packet */ + + LOG_DEBUG(ctx, "using ps reader"); + + /* We are probably dealing with a PS file */ + LOG_FORMAT(ctx, "MPEG PS reader, found start code: 0x%02x%02x%02x%02x", + buffer[0], buffer[1], buffer[2], buffer[3]); + + /* Need to allocate context before searching for streams */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + ctx->priv->module = module; + ctx->tracks = module->tracks; + + /* Store offset so we can get back to what we consider the first pack or + packet */ + module->data_offset = STREAM_POSITION(ctx); + + /* Search for tracks, reset time reference and calculation state first */ + ctx->priv->module->scr_offset = ctx->priv->module->scr = VC_CONTAINER_TIME_UNKNOWN; + ctx->priv->module->searching_tracks = true; + + for (i = 0; i != PS_PACK_SCAN_MAX; ++i) + { + if (buffer[3] == 0xBA && (ps_read_pack_header(ctx) != VC_CONTAINER_SUCCESS)) + goto resync; + + if (ps_read_pes_packet(ctx) == VC_CONTAINER_SUCCESS) + continue; + +resync: + LOG_DEBUG(ctx, "Lost sync, scanning for start code"); + if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_CORRUPTED; + LOG_DEBUG(ctx, "MPEG PS reader, found start code: 0x%"PRIx64" (%"PRId64"): 0x%02x%02x%02x%02x", + STREAM_POSITION(ctx), STREAM_POSITION(ctx), buffer[0], buffer[1], buffer[2], buffer[3]); + } + + /* Seek back to the start of data */ + SEEK(ctx, module->data_offset); + + /* Bail out if we didn't find any tracks */ + if(!ctx->tracks_num) + { + status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE; + goto error; + } + + /* Set data size (necessary for seeking) */ + module->data_size = MAX(ctx->priv->io->size - module->data_offset, INT64_C(0)); + + /* Estimate data rate (necessary for seeking) */ + if(STREAM_SEEKABLE(ctx)) + { + /* Estimate data rate by jumping in the stream */ + #define PS_PACK_SEARCH_MAX 64 + uint64_t position = module->data_offset; + for (i = 0; i != PS_PACK_SEARCH_MAX; ++i) + { + position += (module->data_size / (PS_PACK_SEARCH_MAX + 1)); + SEEK(ctx, position); + + for(;;) + { + if(ps_find_start_code(ctx, buffer) != VC_CONTAINER_SUCCESS) + break; + + if (buffer[3] == 0xBA) + { + if (ps_read_pack_header(ctx) == VC_CONTAINER_SUCCESS) + break; + } + else + { + /* Skip PES packet */ + unsigned length; + SKIP_U32(ctx, "PES packet startcode"); + length = READ_U16(ctx, "PES packet length"); + SKIP_BYTES(ctx, length); + } + } + } + + ctx->duration = (INT64_C(1000000) * module->data_size) / (module->data_rate); + + if (module->scr > module->scr_offset) + { + int64_t delta = (module->scr - module->scr_offset) / INT64_C(27); + + if (delta > ctx->duration) + ctx->duration = delta; + } + + /* Seek back to the start of data */ + SEEK(ctx, module->data_offset); + } + else + { + /* For most files, program_mux_rate is not reliable at all */ + ctx->duration = (INT64_C(100000) * module->data_size) / (INT64_C(5) * module->mux_rate); + } + + /* Reset time reference and calculation state, we're now ready to read data */ + module->scr = VC_CONTAINER_TIME_UNKNOWN; + module->scr_bias = VC_CONTAINER_TIME_UNKNOWN; + + ctx->priv->module->searching_tracks = false; + + if(STREAM_SEEKABLE(ctx)) ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + ctx->priv->pf_close = ps_reader_close; + ctx->priv->pf_read = ps_reader_read; + ctx->priv->pf_seek = ps_reader_seek; + + return STREAM_STATUS(ctx); + + error: + LOG_DEBUG(ctx, "ps: error opening stream (%i)", status); + if(module) ps_reader_close(ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open ps_reader_open +#endif diff --git a/containers/mpga/CMakeLists.txt b/containers/mpga/CMakeLists.txt new file mode 100755 index 0000000..79cf39a --- /dev/null +++ b/containers/mpga/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_mpga ${LIBRARY_TYPE} mpga_reader.c) + +target_link_libraries(reader_mpga containers) + +install(TARGETS reader_mpga DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/mpga/mpga_common.h b/containers/mpga/mpga_common.h new file mode 100755 index 0000000..b8ba8e5 --- /dev/null +++ b/containers/mpga/mpga_common.h @@ -0,0 +1,147 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#define MPGA_HEADER_SIZE 6 + +#define MPGA_MODE_STEREO 0 +#define MPGA_MODE_JSTEREO 1 +#define MPGA_MODE_DUAL 2 +#define MPGA_MODE_MONO 3 + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_read_header( uint8_t frame_header[MPGA_HEADER_SIZE], + uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, + unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, + unsigned int *p_frame_size_samples, unsigned int *p_offset ) +{ + static const uint16_t mpga_bitrate[2][3][15] = + {{{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}, /* MPEG1, Layer 1 */ + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}, /* MPEG1, Layer 2 */ + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}},/* MPEG1, Layer 3 */ + {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, /* MPEG2 and MPEG2.5, Layer 1 */ + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* MPEG2 and MPEG2.5, Layer 2 */ + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}} /* MPEG2 and MPEG2.5, Layer 3 */}; + static const uint16_t mpga_sample_rate[] = {44100, 48000, 32000}; + static const uint16_t mpga_frame_size[] = {384, 1152, 576}; + + unsigned int version, layer, br_id, sr_id, emphasis; + unsigned int bitrate, sample_rate, padding, mode; + + /* Check frame sync, 11 bits as we want to allow for MPEG2.5 */ + if (frame_header[0] != 0xff || (frame_header[1] & 0xe0) != 0xe0) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + version = 4 - ((frame_header[1] >> 3) & 3); + layer = 4 - ((frame_header[1] >> 1) & 3 ); + br_id = (frame_header[2] >> 4) & 0xf; + sr_id = (frame_header[2] >> 2) & 3; + padding = (frame_header[2] >> 1) & 1; + mode = (frame_header[3] >> 6) & 3; + emphasis = (frame_header[3]) & 3; + + /* Check for invalid values */ + if (version == 3 || layer == 4 || br_id == 15 || sr_id == 3 || emphasis == 2) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + if (version == 4) version = 3; + + bitrate = mpga_bitrate[version == 1 ? 0 : 1][layer-1][br_id]; + bitrate *= 1000; + + sample_rate = mpga_sample_rate[sr_id]; + sample_rate >>= (version - 1); + + if (p_version) *p_version = version; + if (p_layer) *p_layer = layer; + if (p_sample_rate) *p_sample_rate = sample_rate; + if (p_channels) *p_channels = mode == MPGA_MODE_MONO ? 1 : 2; + if (p_frame_bitrate) *p_frame_bitrate = bitrate; + if (p_offset) *p_offset = 0; + + if (p_frame_size_samples) + { + *p_frame_size_samples = mpga_frame_size[layer - 1]; + if (version == 1 && layer == 3) *p_frame_size_samples <<= 1; + } + + if (!p_frame_size) + return VC_CONTAINER_SUCCESS; + + if (!bitrate) + *p_frame_size = 0; + else if (layer == 1) + *p_frame_size = (padding + bitrate * 12 / sample_rate) * 4; + else if (layer == 2) + *p_frame_size = padding + bitrate * 144 / sample_rate; + else + *p_frame_size = padding + bitrate * (version == 1 ? 144 : 72) / sample_rate; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T adts_read_header( uint8_t frame_header[MPGA_HEADER_SIZE], + uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, + unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, + unsigned int *p_frame_size_samples, unsigned int *p_offset ) +{ + static const unsigned int adts_sample_rate[16] = + {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350}; + unsigned int profile, sr_id, bitrate, sample_rate, frame_size, channels, crc; + unsigned int frame_size_samples = 1024; + + /* Check frame sync (12 bits) */ + if (frame_header[0] != 0xff || (frame_header[1] & 0xf0) != 0xf0) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + /* Layer must be 0 */ + if ((frame_header[1] >> 1) & 3) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + crc = !(frame_header[1] & 0x1); + profile = (frame_header[2] >> 6) + 1; /* MPEG-4 Audio Object Type */ + + sr_id = (frame_header[2] >> 2) & 0xf; + sample_rate = adts_sample_rate[sr_id]; + channels = ((frame_header[2] & 0x1) << 2) | ((frame_header[3] >> 6) & 0x3); + frame_size = ((frame_header[3] & 0x03) << 11) | (frame_header[4] << 3) | (frame_header[5] >> 5); + + if (!sample_rate || !channels || !frame_size) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + bitrate = frame_size * 8 * sample_rate / frame_size_samples; + + if (p_version) *p_version = profile; + if (p_layer) *p_layer = 0; + if (p_sample_rate) *p_sample_rate = sample_rate; + if (p_channels) *p_channels = channels; + if (p_frame_bitrate) *p_frame_bitrate = bitrate; + if (p_frame_size) *p_frame_size = frame_size; + if (p_frame_size_samples) *p_frame_size_samples = frame_size_samples; + if (p_offset) *p_offset = crc ? 9 : 7; + + return VC_CONTAINER_SUCCESS; +} diff --git a/containers/mpga/mpga_packetizer.c b/containers/mpga/mpga_packetizer.c new file mode 100755 index 0000000..1d7b7bf --- /dev/null +++ b/containers/mpga/mpga_packetizer.c @@ -0,0 +1,288 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Implementation of an MPEG1/2/2.5 audio Layer I/II/III and AAC ADTS packetizer. + */ + +#include +#include + +#include "containers/packetizers.h" +#include "containers/core/packetizers_private.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_time.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_bytestream.h" +#include "mpga_common.h" + +#define MAX_FRAME_SIZE 2881 /* MPEG 2.5 Layer II, 8000 Hz, 160 kbps */ + +VC_CONTAINER_STATUS_T mpga_packetizer_open( VC_PACKETIZER_T * ); + +/*****************************************************************************/ +typedef struct VC_PACKETIZER_MODULE_T { + enum { + STATE_SYNC = 0, + STATE_SYNC_LOST, + STATE_SYNC_NEXT, + STATE_SYNC_DONE, + STATE_HEADER, + STATE_DATA, + } state; + + VC_CONTAINER_STATUS_T (*pf_read_header)( uint8_t frame_header[MPGA_HEADER_SIZE], + uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, + unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, + unsigned int *p_frame_size_samples, unsigned int *p_offset); + + uint32_t frame_size; + unsigned int frame_bitrate; + unsigned int version; + unsigned int layer; + unsigned int sample_rate; + unsigned int channels; + unsigned int frame_size_samples; + unsigned int offset; + + unsigned int lost_sync; + + unsigned int stream_version; + unsigned int stream_layer; + + uint32_t bytes_read; + +} VC_PACKETIZER_MODULE_T; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_packetizer_close( VC_PACKETIZER_T *p_ctx ) +{ + free(p_ctx->priv->module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_packetizer_reset( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + module->lost_sync = 0; + module->state = STATE_SYNC; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_packetizer_packetize( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + VC_CONTAINER_TIME_T *time = &p_ctx->priv->time; + uint8_t header[MPGA_HEADER_SIZE]; + VC_CONTAINER_STATUS_T status; + unsigned int version, layer; + int64_t pts, dts; + + while(1) switch (module->state) + { + case STATE_SYNC_LOST: + bytestream_skip_byte( stream ); + if( !module->lost_sync++ ) + LOG_DEBUG(0, "lost sync"); + module->state = STATE_SYNC; + + case STATE_SYNC: + while( bytestream_peek( stream, header, 2 ) == VC_CONTAINER_SUCCESS ) + { + /* 11 bits sync work (0xffe) */ + if( header[0] == 0xff && (header[1] & 0xe0) == 0xe0 ) + { + module->state = STATE_HEADER; + break; + } + bytestream_skip_byte( stream ); + module->lost_sync++; + } + if( module->state != STATE_HEADER ) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */ + + case STATE_HEADER: + if( bytestream_peek( stream, header, MPGA_HEADER_SIZE ) != VC_CONTAINER_SUCCESS ) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + + status = mpga_read_header( header, + &module->frame_size, &module->frame_bitrate, &module->version, + &module->layer, &module->sample_rate, &module->channels, + &module->frame_size_samples, &module->offset ); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(0, "invalid header"); + module->state = STATE_SYNC_LOST; + break; + } + + /* Version and layer are not allowed to change mid-stream */ + if ((module->stream_version && module->stream_version != module->version) || + (module->stream_layer && module->stream_layer != module->layer)) + { + LOG_ERROR(0, "invalid header"); + module->state = STATE_SYNC_LOST; + break; + } + /* We currently do not support free format streams */ + if (!module->frame_size) + { + LOG_ERROR(0, "free format not supported"); + module->state = STATE_SYNC_LOST; + break; + } + module->state = STATE_SYNC_NEXT; + /* fall through to the next state */ + + case STATE_SYNC_NEXT: + /* To avoid being caught by emulated start codes, we also look at where the next frame is supposed to be */ + if( bytestream_peek_at( stream, module->frame_size, header, MPGA_HEADER_SIZE ) != VC_CONTAINER_SUCCESS ) + { + /* If we know there won't be anymore data then we can just assume + * we've got the frame we're looking for */ + if (flags & VC_PACKETIZER_FLAG_FLUSH) + { + module->state = STATE_SYNC_DONE; + break; + } + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + } + + status = mpga_read_header( header, 0, 0, &version, &layer, 0, 0, 0, 0 ); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(0, "invalid next header"); + module->state = STATE_SYNC_LOST; + break; + } + + /* Version and layer are not allowed to change mid-stream */ + if (module->version != version || module->layer != layer) + { + LOG_ERROR(0, "invalid header"); + module->state = STATE_SYNC_LOST; + break; + } + + module->state = STATE_SYNC_DONE; + /* fall through to the next state */ + + case STATE_SYNC_DONE: + if( module->lost_sync ) + LOG_DEBUG(0, "recovered sync after %i bytes", module->lost_sync); + module->lost_sync = 0; + + bytestream_skip( stream, module->offset ); + module->stream_version = module->version; + module->stream_layer = module->layer; + + vc_container_time_set_samplerate(time, module->sample_rate, 1); + bytestream_get_timestamps(stream, &pts, &dts, true); + + vc_container_time_set(time, pts); + + module->bytes_read = 0; + module->state = STATE_DATA; + /* fall through to the next state */ + + case STATE_DATA: + if( bytestream_size( stream ) < module->frame_size) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + + out->size = module->frame_size - module->bytes_read; + out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; + out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + + if(!module->bytes_read) + { + out->pts = out->dts = vc_container_time_get(time); + out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + + if(flags & VC_PACKETIZER_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + if(flags & VC_PACKETIZER_FLAG_SKIP) + { + bytestream_skip( stream, out->size ); + } + else + { + out->size = MIN(out->size, out->buffer_size); + bytestream_get( stream, out->data, out->size ); + } + module->bytes_read += out->size; + + if(module->bytes_read == module->frame_size) + { + vc_container_time_add(time, module->frame_size_samples); + module->state = STATE_HEADER; + } + return VC_CONTAINER_SUCCESS; + + default: + break; + }; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T mpga_packetizer_open( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module; + + if(p_ctx->in->codec != VC_CONTAINER_CODEC_MPGA && + p_ctx->in->codec != VC_CONTAINER_CODEC_MP4A) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + p_ctx->priv->module = module = malloc(sizeof(*module)); + if(!module) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + + if(p_ctx->in->codec == VC_CONTAINER_CODEC_MPGA) + module->pf_read_header = mpga_read_header; + else + module->pf_read_header = adts_read_header; + + vc_container_format_copy( p_ctx->out, p_ctx->in, 0); + p_ctx->max_frame_size = MAX_FRAME_SIZE; + p_ctx->priv->pf_close = mpga_packetizer_close; + p_ctx->priv->pf_packetize = mpga_packetizer_packetize; + p_ctx->priv->pf_reset = mpga_packetizer_reset; + LOG_DEBUG(0, "using mpeg audio packetizer"); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_PACKETIZER_REGISTER(mpga_packetizer_open, "mpga"); diff --git a/containers/mpga/mpga_reader.c b/containers/mpga/mpga_reader.c new file mode 100755 index 0000000..da5df83 --- /dev/null +++ b/containers/mpga/mpga_reader.c @@ -0,0 +1,618 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#include "mpga_common.h" + +/****************************************************************************** +Defines and constants. +******************************************************************************/ +#define MPGA_XING_HAS_FRAMES 0x00000001 +#define MPGA_XING_HAS_BYTES 0x00000002 +#define MPGA_XING_HAS_TOC 0x00000004 +#define MPGA_XING_HAS_QUALITY 0x00000008 + +#define MPGA_MAX_BAD_FRAMES 4096 /*< Maximum number of failed byte-wise syncs, + should be at least 2881+4 to cover the largest + frame size (MPEG2.5 Layer 2, 160kbit/s 8kHz) + + next frame header */ + +static const unsigned int mpga_sample_rate_adts[16] = +{96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350}; + +static const GUID_T asf_guid_header = +{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}}; + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + uint64_t data_offset; + uint64_t data_size; + uint64_t num_frames; /**< Total number of frames (if known) */ + unsigned int frame_size_samples; /**< Frame size in samples */ + unsigned int bitrate; /**< Bitrate (might change on a per-frame basis if VBR) */ + unsigned int sample_rate; + unsigned int channels; + + /* MPEG audio header information */ + unsigned int version; /**< 1 for MPEG1, 2 for MPEG2, etc. */ + unsigned int layer; + + /* VBR header information */ + uint8_t xing_toc[100]; + int xing_toc_valid; + + /* Per-frame state (updated upon a read or a seek) */ + unsigned int frame_size; + unsigned int frame_data_left; + uint64_t frame_index; + int64_t frame_offset; + int64_t frame_time_pos; /**< pts of current frame */ + unsigned int frame_bitrate; /**< bitrate of current frame */ + + VC_CONTAINER_STATUS_T (*pf_parse_header)( uint8_t frame_header[MPGA_HEADER_SIZE], + uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version, + unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels, + unsigned int *p_frame_size_samples, unsigned int *p_offset); + + uint8_t extradata[2]; /**< codec extra data for aac */ + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static uint32_t PEEK_BYTES_AT( VC_CONTAINER_T *p_ctx, int64_t offset, uint8_t *buffer, int size ) +{ + int ret; + int64_t current_position = STREAM_POSITION(p_ctx); + SEEK(p_ctx, current_position + offset); + ret = PEEK_BYTES(p_ctx, buffer, size); + SEEK(p_ctx, current_position); + return ret; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_check_frame_header( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_MODULE_T *module, uint8_t frame_header[MPGA_HEADER_SIZE] ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + return module->pf_parse_header(frame_header, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_sync( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + uint8_t frame_header[MPGA_HEADER_SIZE]; + uint32_t frame_size; + unsigned int frame_bitrate, version, layer, sample_rate, channels; + unsigned int frame_size_samples, offset; + int sync_count = 0; + + /* If we can't see a full frame header, we treat this as EOS although it + could be a bad stream as well, the caller should distinct between + these two cases */ + if (PEEK_BYTES(p_ctx, (uint8_t*)frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE) + return VC_CONTAINER_ERROR_EOS; + + while (sync_count++ < MPGA_MAX_BAD_FRAMES) + { + status = module->pf_parse_header(frame_header, &frame_size, &frame_bitrate, + &version, &layer, &sample_rate, &channels, + &frame_size_samples, &offset); + if (status == VC_CONTAINER_SUCCESS && + frame_size /* We do not support free format streams */) + { + LOG_DEBUG(p_ctx, "MPEGv%d, layer %d, %d bps, %d Hz", + version, layer, frame_bitrate, sample_rate); + if (PEEK_BYTES_AT(p_ctx, (int64_t)frame_size, frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE || + mpga_check_frame_header(p_ctx, module, frame_header) == VC_CONTAINER_SUCCESS) + break; + + /* If we've reached an ID3 tag then the frame is valid as well */ + if((frame_header[0] == 'I' && frame_header[1] == 'D' && frame_header[2] == '3') || + (frame_header[0] == 'T' && frame_header[1] == 'A' && frame_header[2] == 'G')) + break; + } + else if (status == VC_CONTAINER_SUCCESS) + { + LOG_DEBUG(p_ctx, "free format not supported"); + } + + if (SKIP_BYTES(p_ctx, 1) != 1 || PEEK_BYTES(p_ctx, (uint8_t*)frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE) + return VC_CONTAINER_ERROR_EOS; + } + + if(sync_count > MPGA_MAX_BAD_FRAMES) /* We didn't find a valid frame */ + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + if (module->version) + { + /* FIXME: we don't currently care whether or not the number of channels changes mid-stream */ + if (version != module->version || layer != module->layer) + { + LOG_DEBUG(p_ctx, "version or layer not allowed to change mid-stream"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + } + else + { + module->version = version; + module->layer = layer; + module->sample_rate = sample_rate; + module->channels = channels; + module->frame_size_samples = frame_size_samples; + } + + if(offset) SKIP_BYTES(p_ctx, offset); + module->frame_data_left = module->frame_size = frame_size - offset; + module->frame_bitrate = frame_bitrate; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static int64_t mpga_calculate_frame_time( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + int64_t time; + time = INT64_C(1000000) * module->frame_index * + module->frame_size_samples / module->sample_rate; + return time; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_read_vbr_headers( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[0]; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND; + uint32_t peek_buf[1]; + int64_t offset, start = STREAM_POSITION(p_ctx); + + /* Look for XING header (immediately after layer 3 side information) */ + offset = (module->version == 1) ? ((module->channels == 1) ? INT64_C(21) : INT64_C(36)) : + ((module->channels == 1) ? INT64_C(13) : INT64_C(21)); + + if (PEEK_BYTES_AT(p_ctx, offset, (uint8_t*)peek_buf, 4) != 4) + return VC_CONTAINER_ERROR_FORMAT_INVALID; /* File would be way too small */ + + if (peek_buf[0] == VC_FOURCC('X','i','n','g') || peek_buf[0] == VC_FOURCC('I','n','f','o')) + { + uint32_t flags = 0, num_frames = 0, data_size = 0; + + /* If the first frame has a XING header then we know it's a valid (but empty) audio + frame so we safely parse the header whilst skipping to the next frame */ + SKIP_BYTES(p_ctx, offset); /* FIXME: we don't care about layer 3 side information? */ + + SKIP_FOURCC(p_ctx, "XING"); + flags = READ_U32(p_ctx, "XING flags"); + + if (flags & MPGA_XING_HAS_FRAMES) + num_frames = READ_U32(p_ctx, "XING frames"); + + if (flags & MPGA_XING_HAS_BYTES) + data_size = READ_U32(p_ctx, "XING bytes"); + + if (flags & MPGA_XING_HAS_TOC) + { + READ_BYTES(p_ctx, module->xing_toc, sizeof(module->xing_toc)); + /* TOC is useful only if we know the number of frames */ + if (num_frames) module->xing_toc_valid = 1; + /* Ensure time zero points to first frame even if TOC is broken */ + module->xing_toc[0] = 0; + } + + if (flags & MPGA_XING_HAS_QUALITY) + SKIP_U32(p_ctx, "XING quality"); + + module->data_size = data_size; + module->num_frames = num_frames; + + if (module->num_frames && module->data_size) + { + /* We can calculate average bitrate */ + module->bitrate = + module->data_size * module->sample_rate * 8 / (module->num_frames * module->frame_size_samples); + } + + p_ctx->duration = (module->num_frames * module->frame_size_samples * 1000000LL) / module->sample_rate; + + /* Look for additional LAME header (follows XING) */ + if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4) + return VC_CONTAINER_ERROR_FORMAT_INVALID; /* File would still be way too small */ + + if (peek_buf[0] == VC_FOURCC('L','A','M','E')) + { + uint32_t encoder_delay; + + SKIP_FOURCC(p_ctx, "LAME"); + SKIP_STRING(p_ctx, 5, "LAME encoder version"); + SKIP_U8(p_ctx, "LAME tag revision/VBR method"); + SKIP_U8(p_ctx, "LAME LP filter value"); + SKIP_U32(p_ctx, "LAME peak signal amplitude"); + SKIP_U16(p_ctx, "LAME radio replay gain"); + SKIP_U16(p_ctx, "LAME audiophile replay gain"); + SKIP_U8(p_ctx, "LAME encoder flags"); + SKIP_U8(p_ctx, "LAME ABR/minimal bitrate"); + encoder_delay = READ_U24(p_ctx, "LAME encoder delay/padding"); + SKIP_U8(p_ctx, "LAME misc"); + SKIP_U8(p_ctx, "LAME MP3 gain"); + SKIP_U16(p_ctx, "LAME presets and surround info"); + SKIP_U32(p_ctx, "LAME music length"); + SKIP_U16(p_ctx, "LAME music CRC"); + SKIP_U16(p_ctx, "LAME tag CRC"); + track->format->type->audio.gap_delay = (encoder_delay >> 12) + module->frame_size_samples; + track->format->type->audio.gap_padding = encoder_delay & 0xfff; + } + + SEEK(p_ctx, start); + status = VC_CONTAINER_SUCCESS; + } + + /* FIXME: if not success, try to read 'VBRI' header */ + + return status; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[0]; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if (module->frame_data_left == 0) + { + status = mpga_sync(p_ctx); + if (status != VC_CONTAINER_SUCCESS) goto error; + } + + if (module->bitrate) + { + /* Simple moving average over bitrate values seen so far */ + module->bitrate = (module->bitrate * 31 + module->frame_bitrate) >> 5; + } + else + { + module->bitrate = module->frame_bitrate; + } + + /* Check if we can skip the frame straight-away */ + if (!track->is_enabled || + ((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO))) + { + /* Just skip the frame */ + SKIP_BYTES(p_ctx, module->frame_size); + module->frame_data_left = 0; + if(!track->is_enabled) + status = VC_CONTAINER_ERROR_CONTINUE; + goto end; + } + + /* Fill in packet information */ + p_packet->flags = p_packet->track = 0; + if (module->frame_data_left == module->frame_size) + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME; + else + p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + + p_packet->size = module->frame_data_left; + + p_packet->pts = module->frame_time_pos; + p_packet->dts = VC_CONTAINER_TIME_UNKNOWN; + + if ((flags & VC_CONTAINER_READ_FLAG_SKIP)) + { + SKIP_BYTES(p_ctx, module->frame_size); + module->frame_data_left = 0; + goto end; + } + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + p_packet->size = MIN(p_packet->buffer_size, module->frame_data_left); + p_packet->size = READ_BYTES(p_ctx, p_packet->data, p_packet->size); + module->frame_data_left -= p_packet->size; + + end: + if (module->frame_data_left == 0) + { + module->frame_index++; + module->frame_offset += module->frame_size; + module->frame_time_pos = mpga_calculate_frame_time(p_ctx); + +#if 0 /* FIXME: is this useful e.g. progressive download? */ + module->num_frames = MAX(module->num_frames, module->frame_index); + module->data_size = MAX(module->data_size, module->frame_offset); + p_ctx->duration = MAX(p_ctx->duration, mpga_calculate_frame_time(p_ctx)); +#endif + } + + return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status; + +error: + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_reader_seek( VC_CONTAINER_T *p_ctx, + int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint64_t seekpos, position = STREAM_POSITION(p_ctx); + VC_CONTAINER_PARAM_UNUSED(flags); + + if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(p_ctx)) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + if (*p_offset != INT64_C(0)) + { + if (!p_ctx->duration) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + if (module->xing_toc_valid) + { + int64_t ppm; + int percent, lower, upper, delta; + + ppm = (*p_offset * module->sample_rate) / (module->num_frames * module->frame_size_samples); + ppm = MIN(ppm, INT64_C(999999)); + + percent = ppm / 10000; + delta = ppm % 10000; + + lower = module->xing_toc[percent]; + upper = percent < 99 ? module->xing_toc[percent + 1] : 256; + + seekpos = module->data_offset + + (((module->data_size * lower) + (module->data_size * (upper - lower) * delta) / 10000) >> 8); + } + else + { + /* The following will be accurate for CBR only */ + seekpos = module->data_offset + (*p_offset * module->data_size) / p_ctx->duration; + } + } + else + { + seekpos = module->data_offset; + } + + SEEK(p_ctx, seekpos); + status = mpga_sync(p_ctx); + if (status && status != VC_CONTAINER_ERROR_EOS) + goto error; + + module->frame_index = (*p_offset * module->num_frames + (p_ctx->duration >> 1)) / p_ctx->duration; + module->frame_offset = STREAM_POSITION(p_ctx) - module->data_offset; + + *p_offset = module->frame_time_pos = mpga_calculate_frame_time(p_ctx); + + return STREAM_STATUS(p_ctx); + +error: + SEEK(p_ctx, position); + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpga_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + if (p_ctx->tracks_num != 0) + vc_container_free_track(p_ctx, p_ctx->tracks[0]); + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + free(module); + p_ctx->priv->module = 0; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T *p_ctx ) +{ + const char *extension = vc_uri_path_extension(p_ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_T *track = NULL; + unsigned int i; + GUID_T guid; + + /* Check if the user has specified a container */ + vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension); + + /* Since mpeg audio is difficult to auto-detect, we use the extension as + part of the autodetection */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "mp3") && strcasecmp(extension, "mp2") && + strcasecmp(extension, "aac") && strcasecmp(extension, "adts")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Check we're not in fact dealing with an ASF file */ + if(PEEK_BYTES(p_ctx, (uint8_t *)&guid, sizeof(guid)) == sizeof(guid) && + !memcmp(&guid, &asf_guid_header, sizeof(guid))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + LOG_DEBUG(p_ctx, "using mpga reader"); + + /* Allocate our context */ + if ((module = malloc(sizeof(*module))) == NULL) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = &module->track; + + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + p_ctx->tracks_num = 1; + + module->pf_parse_header = mpga_read_header; + if(!strcasecmp(extension, "aac") || !strcasecmp(extension, "adts")) + module->pf_parse_header = adts_read_header; + + if ((status = mpga_sync(p_ctx)) != VC_CONTAINER_SUCCESS) + { + /* An error here probably means it's not an mpga file at all */ + if(status == VC_CONTAINER_ERROR_FORMAT_INVALID) + status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + goto error; + } + + /* If we got this far, we're probably dealing with an mpeg audio file */ + track = p_ctx->tracks[0]; + track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + track->format->codec = VC_CONTAINER_CODEC_MPGA; + if(module->pf_parse_header == adts_read_header) + { + uint8_t *extra = track->format->extradata = module->extradata; + unsigned int sr_id; + for( sr_id = 0; sr_id < 13; sr_id++ ) + if( mpga_sample_rate_adts[sr_id] == module->sample_rate ) break; + extra[0] = (module->version << 3) | ((sr_id & 0xe) >> 1); + extra[1] = ((sr_id & 0x1) << 7) | (module->channels << 3); + track->format->extradata_size = 2; + track->format->codec = VC_CONTAINER_CODEC_MP4A; + } + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + track->is_enabled = true; + track->format->type->audio.channels = module->channels; + track->format->type->audio.sample_rate = module->sample_rate; + track->format->type->audio.bits_per_sample = 0; + track->format->type->audio.block_align = 1; + + module->data_offset = STREAM_POSITION(p_ctx); + + /* Look for VBR headers within the first frame */ + status = mpga_read_vbr_headers(p_ctx); + if (status && status != VC_CONTAINER_ERROR_NOT_FOUND) goto error; + + /* If we couldn't get this information from VBR headers, try to determine + file size, bitrate, number of frames and duration */ + if (!module->data_size) + module->data_size = MAX(p_ctx->priv->io->size - module->data_offset, INT64_C(0)); + + if (!module->bitrate) + { + if (STREAM_SEEKABLE(p_ctx)) + { + /* Scan past a few hundred frames (audio will often have + silence in the beginning so we need to see more than + just a few frames) and estimate bitrate */ + for (i = 0; i < 256; ++i) + if (mpga_reader_read(p_ctx, NULL, VC_CONTAINER_READ_FLAG_SKIP)) break; + /* Seek back to start of data */ + SEEK(p_ctx, module->data_offset); + module->frame_index = 0; + module->frame_offset = INT64_C(0); + module->frame_time_pos = mpga_calculate_frame_time(p_ctx); + } + else + { + /* Bitrate will be correct for CBR only */ + module->bitrate = module->frame_bitrate; + } + } + + track->format->bitrate = module->bitrate; + + if (!module->num_frames) + { + module->num_frames = (module->data_size * module->sample_rate * 8LL) / + (module->bitrate * module->frame_size_samples); + } + + if (!p_ctx->duration && module->bitrate) + { + p_ctx->duration = (INT64_C(8000000) * module->data_size) / module->bitrate; + } + + p_ctx->priv->pf_close = mpga_reader_close; + p_ctx->priv->pf_read = mpga_reader_read; + p_ctx->priv->pf_seek = mpga_reader_seek; + + if(STREAM_SEEKABLE(p_ctx)) p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) goto error; + return VC_CONTAINER_SUCCESS; + +error: + if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS) + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + LOG_DEBUG(p_ctx, "error opening stream (%i)", status); + if (p_ctx->tracks_num != 0) + vc_container_free_track(p_ctx, p_ctx->tracks[0]); + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + if (module) free(module); + p_ctx->priv->module = NULL; + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open mpga_reader_open +#endif diff --git a/containers/mpgv/mpgv_packetizer.c b/containers/mpgv/mpgv_packetizer.c new file mode 100755 index 0000000..79ccf78 --- /dev/null +++ b/containers/mpgv/mpgv_packetizer.c @@ -0,0 +1,431 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Implementation of an MPEG1/2 video packetizer. + */ + +#include +#include + +#include "containers/packetizers.h" +#include "containers/core/packetizers_private.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_time.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_bytestream.h" + +/** Arbitrary number which should be sufficiently high so that no sane frame will + * be bigger than that. */ +#define MAX_FRAME_SIZE (1920*1088*2) + +static uint8_t mpgv_startcode[3] = {0x0, 0x0, 0x1}; + +#define PICTURE_CODING_TYPE_I 0x1 +#define PICTURE_CODING_TYPE_P 0x2 +#define PICTURE_CODING_TYPE_B 0x3 + +VC_CONTAINER_STATUS_T mpgv_packetizer_open( VC_PACKETIZER_T * ); + +/*****************************************************************************/ +typedef struct VC_PACKETIZER_MODULE_T { + enum { + STATE_SYNC = 0, + STATE_SYNC_NEXT, + STATE_FRAME_DONE, + STATE_UNIT_HEADER, + STATE_UNIT_SEQUENCE, + STATE_UNIT_GROUP, + STATE_UNIT_PICTURE, + STATE_UNIT_SLICE, + STATE_UNIT_OTHER, + STATE_DATA, + } state; + + size_t frame_size; + size_t unit_offset; + + unsigned int seen_sequence_header; + unsigned int seen_picture_header; + unsigned int seen_slice; + unsigned int lost_sync; + + unsigned int picture_type; + unsigned int picture_temporal_ref; + int64_t pts; + int64_t dts; + + uint32_t bytes_read; + + unsigned int width, height; + unsigned int frame_rate_num, frame_rate_den; + unsigned int aspect_ratio_num, aspect_ratio_den; + bool low_delay; + +} VC_PACKETIZER_MODULE_T; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_packetizer_close( VC_PACKETIZER_T *p_ctx ) +{ + free(p_ctx->priv->module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_packetizer_reset( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + module->lost_sync = 0; + module->state = STATE_SYNC; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_read_sequence_header(VC_CONTAINER_BYTESTREAM_T *stream, + size_t offset, unsigned int *width, unsigned int *height, + unsigned int *frame_rate_num, unsigned int *frame_rate_den, + unsigned int *aspect_ratio_num, unsigned int *aspect_ratio_den) +{ + static const int frame_rate[16][2] = + { {0, 0}, {24000, 1001}, {24, 1}, {25, 1}, {30000, 1001}, {30, 1}, {50, 1}, + {60000, 1001}, {60, 1}, + /* Unofficial values */ + {15, 1001}, /* From Xing */ + {5, 1001}, {10, 1001}, {12, 1001}, {15, 1001} /* From libmpeg3 */ }; + static const int aspect_ratio[16][2] = + { {0, 0}, {1, 1}, {4, 3}, {16, 9}, {221, 100} }; + + VC_CONTAINER_STATUS_T status; + unsigned int w, h, fr, ar; + int64_t ar_num, ar_den, div; + uint8_t header[8]; + + status = bytestream_peek_at( stream, offset, header, sizeof(header)); + if(status != VC_CONTAINER_SUCCESS) + return status; + + w = (header[4] << 4) | (header[5] >> 4); + h = ((header[5]&0x0f) << 8) | header[6]; + ar = header[7] >> 4; + fr = header[7]&0x0f; + if (!w || !h || !ar || !fr) + return VC_CONTAINER_ERROR_CORRUPTED; + + *width = w; + *height = h; + *frame_rate_num = frame_rate[fr][0]; + *frame_rate_den = frame_rate[fr][1]; + ar_num = (int64_t)aspect_ratio[ar][0] * h; + ar_den = (int64_t)aspect_ratio[ar][1] * w; + div = vc_container_maths_gcd(ar_num, ar_den); + if (div) + { + ar_num /= div; + ar_den /= div; + } + *aspect_ratio_num = ar_num; + *aspect_ratio_den = ar_den; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_read_picture_header(VC_CONTAINER_BYTESTREAM_T *stream, + size_t offset, unsigned int *type, unsigned int *temporal_ref) +{ + VC_CONTAINER_STATUS_T status; + uint8_t h[2]; + + status = bytestream_peek_at(stream, offset + sizeof(mpgv_startcode) + 1, h, sizeof(h)); + if(status != VC_CONTAINER_SUCCESS) + return status; + + *temporal_ref = (h[0] << 2) | (h[1] >> 6); + *type = (h[1] >> 3) & 0x7; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_update_format( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + + LOG_DEBUG(0, "mpgv format: width %i, height %i, rate %i/%i, ar %i/%i", + module->width, module->height, module->frame_rate_num, module->frame_rate_den, + module->aspect_ratio_num, module->aspect_ratio_den); + + p_ctx->out->type->video.width = p_ctx->out->type->video.visible_width = module->width; + p_ctx->out->type->video.height = p_ctx->out->type->video.visible_height = module->height; + p_ctx->out->type->video.par_num = module->aspect_ratio_num; + p_ctx->out->type->video.par_den = module->aspect_ratio_den; + p_ctx->out->type->video.frame_rate_num = module->frame_rate_num; + p_ctx->out->type->video.frame_rate_den = module->frame_rate_den; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T mpgv_packetizer_packetize( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + VC_CONTAINER_TIME_T *time = &p_ctx->priv->time; + VC_CONTAINER_STATUS_T status; + uint8_t header[4]; + size_t offset; + + while(1) switch (module->state) + { + case STATE_SYNC: + offset = 0; + status = bytestream_find_startcode( stream, &offset, + mpgv_startcode, sizeof(mpgv_startcode) ); + + if(offset && !module->lost_sync) + LOG_DEBUG(0, "lost sync"); + + bytestream_skip(stream, offset); + module->lost_sync += offset; + + if(status != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */ + + if(module->lost_sync) + LOG_DEBUG(0, "recovered sync after %i bytes", module->lost_sync); + module->lost_sync = 0; + module->state = STATE_UNIT_HEADER; + module->frame_size = 0; + module->unit_offset = 0; + /* fall through to the next state */ + + case STATE_UNIT_HEADER: + status = bytestream_peek_at( stream, module->unit_offset, header, sizeof(header)); + if(status != VC_CONTAINER_SUCCESS) + { + if (!(flags & VC_PACKETIZER_FLAG_FLUSH) || + !module->seen_picture_header || !module->seen_slice) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + module->state = STATE_FRAME_DONE; + break; + } + +#if defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE) + LOG_DEBUG(0, "found unit (%x)", header[3]); +#endif + + /* Detect start of new frame */ + if(module->seen_picture_header && module->seen_slice && + (header[3] == 0x00 /* A picture header */ || + (header[3] > 0xAF && header[3] != 0xB7) /* Not a slice or sequence end */)) + { + module->state = STATE_FRAME_DONE; + break; + } + + module->frame_size += sizeof(mpgv_startcode); + module->state = STATE_SYNC_NEXT; + /* fall through to the next state */ + + case STATE_SYNC_NEXT: + status = bytestream_find_startcode( stream, &module->frame_size, + mpgv_startcode, sizeof(mpgv_startcode) ); + + /* Sanity check the size of frames. This makes sure we don't endlessly accumulate data + * to make up a new frame. */ + if(module->frame_size > p_ctx->max_frame_size) + { + LOG_ERROR(0, "frame too big (%i/%i), dropping", module->frame_size, p_ctx->max_frame_size); + bytestream_skip(stream, module->frame_size); + module->state = STATE_SYNC; + break; + } + if(status != VC_CONTAINER_SUCCESS) + { + if (!(flags & VC_PACKETIZER_FLAG_FLUSH) || + !module->seen_picture_header || !module->seen_slice) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + module->state = STATE_FRAME_DONE; + break; + } + + bytestream_peek_at( stream, module->unit_offset, header, sizeof(header)); + + /* Drop everything until we've seen a sequence header */ + if(header[3] != 0xB3 && !module->seen_sequence_header) + { + LOG_DEBUG(0, "waiting for sequence header, dropping %i bytes", module->frame_size); + module->state = STATE_UNIT_HEADER; + bytestream_skip(stream, module->frame_size); + module->unit_offset = module->frame_size = 0; + break; + } + + if(header[3] == 0x00) + module->state = STATE_UNIT_PICTURE; + else if(header[3] >= 0x01 && header[3] <= 0xAF) + module->state = STATE_UNIT_SLICE; + else if(header[3] == 0xB3) + module->state = STATE_UNIT_SEQUENCE; + else if(header[3] == 0xB8) + module->state = STATE_UNIT_GROUP; + else + module->state = STATE_UNIT_OTHER; + break; + + case STATE_UNIT_SEQUENCE: + status = mpgv_read_sequence_header(stream, module->unit_offset, &module->width, &module->height, + &module->frame_rate_num, &module->frame_rate_den, &module->aspect_ratio_num, &module->aspect_ratio_den); + if(status != VC_CONTAINER_SUCCESS && !module->seen_sequence_header) + { + /* We need a sequence header so drop everything until we see one */ + LOG_DEBUG(0, "invalid first sequence header, dropping %i bytes", module->frame_size); + bytestream_skip(stream, module->frame_size); + module->state = STATE_SYNC; + break; + } + mpgv_update_format(p_ctx); + module->seen_sequence_header = true; + vc_container_time_set_samplerate(time, module->frame_rate_num, module->frame_rate_den); + module->state = STATE_UNIT_HEADER; + module->unit_offset = module->frame_size; + break; + + case STATE_UNIT_PICTURE: + status = mpgv_read_picture_header(stream, module->unit_offset, &module->picture_type, &module->picture_temporal_ref); + if(status != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + module->seen_picture_header = true; + module->state = STATE_UNIT_HEADER; + module->unit_offset = module->frame_size; + break; + + case STATE_UNIT_SLICE: + module->seen_slice = true; + module->state = STATE_UNIT_HEADER; + module->unit_offset = module->frame_size; + break; + + case STATE_UNIT_GROUP: + case STATE_UNIT_OTHER: + module->state = STATE_UNIT_HEADER; + module->unit_offset = module->frame_size; + break; + + case STATE_FRAME_DONE: + bytestream_get_timestamps(stream, &module->pts, &module->dts, false); + + if(module->picture_type == PICTURE_CODING_TYPE_B || module->low_delay) + { + if(module->pts == VC_CONTAINER_TIME_UNKNOWN) + module->pts = module->dts; + if(module->dts == VC_CONTAINER_TIME_UNKNOWN) + module->dts = module->pts; + } + vc_container_time_set(time, module->pts); + + module->bytes_read = 0; + module->state = STATE_DATA; + module->seen_slice = false; + module->seen_picture_header = false; + +#if defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE) + LOG_DEBUG(0, "new frame, type %x, size %i, temp_ref %i)", module->picture_type, + module->frame_size, module->picture_temporal_ref); +#endif + /* fall through to the next state */ + + case STATE_DATA: + out->size = module->frame_size - module->bytes_read; + out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; + out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + + if(!module->bytes_read) + { + out->pts = module->pts; + out->dts = module->dts; + out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + + if(flags & VC_PACKETIZER_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + if(flags & VC_PACKETIZER_FLAG_SKIP) + { + bytestream_skip( stream, out->size ); + } + else + { + out->size = MIN(out->size, out->buffer_size); + bytestream_get( stream, out->data, out->size ); + } + module->bytes_read += out->size; + + if(module->bytes_read == module->frame_size) + { + vc_container_time_add(time, 1); + module->state = STATE_UNIT_HEADER; + module->frame_size = 0; + module->unit_offset = 0; + } + else + out->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + return VC_CONTAINER_SUCCESS; + + default: + break; + }; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T mpgv_packetizer_open( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module; + + if(p_ctx->in->codec != VC_CONTAINER_CODEC_MP1V && + p_ctx->in->codec != VC_CONTAINER_CODEC_MP2V) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + p_ctx->priv->module = module = malloc(sizeof(*module)); + if(!module) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + + vc_container_format_copy( p_ctx->out, p_ctx->in, 0); + p_ctx->max_frame_size = MAX_FRAME_SIZE; + p_ctx->priv->pf_close = mpgv_packetizer_close; + p_ctx->priv->pf_packetize = mpgv_packetizer_packetize; + p_ctx->priv->pf_reset = mpgv_packetizer_reset; + LOG_DEBUG(0, "using mpeg video packetizer"); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_PACKETIZER_REGISTER(mpgv_packetizer_open, "mpgv"); diff --git a/containers/net/net_sockets.h b/containers/net/net_sockets.h new file mode 100755 index 0000000..e5a97a2 --- /dev/null +++ b/containers/net/net_sockets.h @@ -0,0 +1,263 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_NET_SOCKETS_H +#define VC_NET_SOCKETS_H + +/** \file net_sockets.h + * Abstraction layer for socket-style network communication, to enable porting + * between platforms. + * + * Does not support IPv6 multicast. + */ + +#include "containers/containers_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Status codes that can occur in a socket instance. */ +typedef enum { + VC_CONTAINER_NET_SUCCESS = 0, /**< No error */ + VC_CONTAINER_NET_ERROR_GENERAL, /**< An unrecognised error has occurred */ + VC_CONTAINER_NET_ERROR_INVALID_SOCKET, /**< Invalid socket passed to function */ + VC_CONTAINER_NET_ERROR_NOT_ALLOWED, /**< The operation requested is not allowed */ + VC_CONTAINER_NET_ERROR_INVALID_PARAMETER, /**< An invalid parameter was passed in */ + VC_CONTAINER_NET_ERROR_NO_MEMORY, /**< Failure due to lack of memory */ + VC_CONTAINER_NET_ERROR_ACCESS_DENIED, /**< Permission denied */ + VC_CONTAINER_NET_ERROR_TOO_BIG, /**< Too many handles already open */ + VC_CONTAINER_NET_ERROR_WOULD_BLOCK, /**< Asynchronous operation would block */ + VC_CONTAINER_NET_ERROR_IN_PROGRESS, /**< An operation is already in progress on this socket */ + VC_CONTAINER_NET_ERROR_IN_USE, /**< The address/port is already in use */ + VC_CONTAINER_NET_ERROR_NETWORK, /**< Network is unavailable */ + VC_CONTAINER_NET_ERROR_CONNECTION_LOST, /**< The connection has been lost, closed by network, etc. */ + VC_CONTAINER_NET_ERROR_NOT_CONNECTED, /**< The socket is not connected */ + VC_CONTAINER_NET_ERROR_TIMED_OUT, /**< Operation timed out */ + VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED, /**< Connection was refused by target */ + VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND, /**< Target address could not be resolved */ + VC_CONTAINER_NET_ERROR_TRY_AGAIN, /**< A temporary failure occurred that may clear */ +} vc_container_net_status_t; + +/** Operations that can be applied to sockets */ +typedef enum { + /** Set the buffer size used on the socket + * arg1: uint32_t - New buffer size in bytes */ + VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE = 1, + /** Set the timeout to be used on read operations + * arg1: uint32_t - New timeout in milliseconds, or INFINITE_TIMEOUT_MS */ + VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, +} vc_container_net_control_t; + +/** Container Input / Output Context. + * This is an opaque structure that defines the context for a socket instance. + * The details of the structure are contained within the platform implementation. */ +typedef struct vc_container_net_tag VC_CONTAINER_NET_T; + +/** \name Socket open flags + * The following flags can be used when opening a network socket. */ +/* @{ */ +typedef uint32_t vc_container_net_open_flags_t; +/** Connected stream socket, rather than connectionless datagram socket */ +#define VC_CONTAINER_NET_OPEN_FLAG_STREAM 1 +/** Force use of IPv4 addressing */ +#define VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4 2 +/** Force use of IPv6 addressing */ +#define VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6 6 +/** Use IPv4 broadcast address for datagram delivery */ +#define VC_CONTAINER_NET_OPEN_FLAG_IP4_BROADCAST 8 +/* @} */ + +/** Mask of bits used in forcing address type */ +#define VC_CONTAINER_NET_OPEN_FLAG_FORCE_MASK 6 + +/** Blocks until data is available, or an error occurs. + * Used with the VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS control operation. */ +#define INFINITE_TIMEOUT_MS 0xFFFFFFFFUL + + +/** Opens a network socket instance. + * The network address can be a host name, dotted IP4, hex IP6 address or NULL. Passing NULL + * signifies the socket is either to be used as a datagram receiver or a stream server, + * depending on the flags. + * \ref VC_CONTAINER_NET_OPEN_FLAG_STREAM will open the socket for connected streaming. The default + * is to use connectionless datagrams. + * \ref VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4 will force the use of IPv4 addressing or fail to open + * the socket. The default is to pick the first available. + * \ref VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6 will force the use of IPv6 addressing or fail to open + * the socket. The default is to pick the first available. + * \ref VC_CONTAINER_NET_OPEN_FLAG_IP4_BROADCAST will use IPv4 broadcast addressing for a datagram + * sender. Use with an IPv6 address, stream socket or datagram receiver will raise an error. + * If the p_status parameter is not NULL, the status code will be written to it to indicate the + * reason for failure, or VC_CONTAINER_NET_SUCCESS on success. + * Sockets shall be bound and connected as necessary. Stream server sockets shall further need + * to have vc_container_net_listen and vc_container_net_accept called on them before data can be transferred. + * + * \param address Network address or NULL. + * \param port Network port or well-known name. This is the local port for receivers/servers. + * \param flags Flags controlling socket type. + * \param p_status Optional pointer to variable to receive status of operation. + * \return The socket instance or NULL on error. */ +VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port, + vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status ); + +/** Closes a network socket instance. + * The p_ctx pointer must not be used after it has been closed. + * + * \param p_ctx The socket instance to close. + * \return The status code for closing the socket. */ +vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx ); + +/** Query the latest status of the socket. + * + * \param p_ctx The socket instance. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx ); + +/** Read data from the socket. + * The function will read up to the requested number of bytes into the buffer. + * If there is no data immediately available to read, the function will block + * until data arrives, an error occurs or the timeout is reached (if set). + * When the function returns zero, the socket may have been closed, an error + * may have occurred, a zero length datagram received, or the timeout reached. + * Check vc_container_net_status() to differentiate. + * Attempting to read on a datagram sender socket will trigger an error. + * + * \param p_ctx The socket instance. + * \param buffer The buffer into which bytes will be read. + * \param size The maximum number of bytes to read. + * \return The number of bytes actually read. */ +size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size ); + +/** Write data to the socket. + * If the socket cannot send the requested number of bytes in one go, the function + * will return a value smaller than size. + * Attempting to write on a datagram receiver socket will trigger an error. + * + * \param p_ctx The socket instance. + * \param buffer The buffer from which bytes will be written. + * \param size The maximum number of bytes to write. + * \return The number of bytes actually written. */ +size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size ); + +/** Start a stream server socket listening for connections from clients. + * Attempting to use this on anything other than a stream server socket shall + * trigger an error. + * + * \param p_ctx The socket instance. + * \param maximum_connections The maximum number of queued connections to allow. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections ); + +/** Accept a client connection on a listening stream server socket. + * Attempting to use this on anything other than a listening stream server socket + * shall trigger an error. + * When a client connection is made, the new instance representing it is returned + * via pp_client_ctx. + * + * \param p_server ctx The server socket instance. + * \param pp_client_ctx The address where the pointer to the new client's socket + * instance is written. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx ); + +/** Non-blocking check for data being available to read. + * If an error occurs, the function will return false and the error can be + * obtained using socket_status(). + * + * \param p_ctx The socket instance. + * \return True if there is data available to read immediately. */ +bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx ); + +/** Returns the maximum size of a datagram in bytes, for sending or receiving. + * The limit for reading from or writing to stream sockets will generally be + * greater than this value, although the call can also be made on such sockets. + * + * \param p_ctx The socket instance. + * \return The maximum size of a datagram in bytes. */ +size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx ); + +/** Get the DNS name or IP address of a stream server client, if connected. + * The length of the name will be limited by name_len, taking into account a + * terminating NUL character. + * Calling this function on a non-stream server instance, or one that is not + * connected to a client, will result in an error status. + * + * \param p_ctx The socket instance. + * \param name Pointer where the name should be written. + * \param name_len Maximum number of characters to write to name. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len ); + +/** Get the port of a stream server client, if connected. + * The port is written to the address in host order. + * Calling this function on a non-stream server instance, or one that is not + * connected to a client, will result in an error status. + * + * \param p_ctx The socket instance. + * \param port Pointer where the port should be written. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port ); + +/** Perform a control operation on the socket. + * See vc_container_net_control_t for more details. + * + * \param p_ctx The socket instance. + * \param operation The control operation to perform. + * \param args Variable list of additional arguments to the operation. + * \return The status of the socket. */ +vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx, vc_container_net_control_t operation, va_list args); + +/** Convert a 32-bit unsigned value from network order (big endian) to host order. + * + * \param value The value to be converted. + * \return The converted value. */ +uint32_t vc_container_net_to_host( uint32_t value ); + +/** Convert a 32-bit unsigned value from host order to network order (big endian). + * + * \param value The value to be converted. + * \return The converted value. */ +uint32_t vc_container_net_from_host( uint32_t value ); + +/** Convert a 16-bit unsigned value from network order (big endian) to host order. + * + * \param value The value to be converted. + * \return The converted value. */ +uint16_t vc_container_net_to_host_16( uint16_t value ); + +/** Convert a 16-bit unsigned value from host order to network order (big endian). + * + * \param value The value to be converted. + * \return The converted value. */ +uint16_t vc_container_net_from_host_16( uint16_t value ); + +#ifdef __cplusplus +} +#endif + +#endif /* VC_NET_SOCKETS_H */ diff --git a/containers/net/net_sockets_bsd.c b/containers/net/net_sockets_bsd.c new file mode 100755 index 0000000..4503a9a --- /dev/null +++ b/containers/net/net_sockets_bsd.c @@ -0,0 +1,117 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "net_sockets.h" +#include "net_sockets_priv.h" +#include "containers/core/containers_common.h" + +/*****************************************************************************/ + +/** Default maximum datagram size. + * This is based on the default Ethernet MTU size, less the IP and UDP headers. + */ +#define DEFAULT_MAXIMUM_DATAGRAM_SIZE (1500 - 20 - 8) + +/** Maximum socket buffer size to use. */ +#define MAXIMUM_BUFFER_SIZE 65536 + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_private_last_error() +{ + switch (errno) + { + case EACCES: return VC_CONTAINER_NET_ERROR_ACCESS_DENIED; + case EFAULT: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case EINVAL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case EMFILE: return VC_CONTAINER_NET_ERROR_TOO_BIG; + case EWOULDBLOCK: return VC_CONTAINER_NET_ERROR_WOULD_BLOCK; + case EINPROGRESS: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; + case EALREADY: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; + case EADDRINUSE: return VC_CONTAINER_NET_ERROR_IN_USE; + case EADDRNOTAVAIL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case ENETDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; + case ENETUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; + case ENETRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case ECONNABORTED: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case ECONNRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case ENOBUFS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case ENOTCONN: return VC_CONTAINER_NET_ERROR_NOT_CONNECTED; + case ESHUTDOWN: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case ETIMEDOUT: return VC_CONTAINER_NET_ERROR_TIMED_OUT; + case ECONNREFUSED: return VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED; + case ELOOP: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case ENAMETOOLONG: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case EHOSTDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; + case EHOSTUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; + case EUSERS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case EDQUOT: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case ESTALE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + /* All other errors are unexpected, so just map to a general purpose error code. */ + default: + return VC_CONTAINER_NET_ERROR_GENERAL; + } +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_private_init() +{ + /* No additional initialization required */ + return VC_CONTAINER_NET_SUCCESS; +} + +/*****************************************************************************/ +void vc_container_net_private_deinit() +{ + /* No additional deinitialization required */ +} + +/*****************************************************************************/ +void vc_container_net_private_close( SOCKET_T sock ) +{ + close(sock); +} + +/*****************************************************************************/ +void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable ) +{ + int opt = enable ? 1 : 0; + + (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); +} + +/*****************************************************************************/ +size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock ) +{ + (void)sock; + + /* No easy way to determine this, just use the default. */ + return DEFAULT_MAXIMUM_DATAGRAM_SIZE; +} diff --git a/containers/net/net_sockets_bsd.h b/containers/net/net_sockets_bsd.h new file mode 100755 index 0000000..fd554c9 --- /dev/null +++ b/containers/net/net_sockets_bsd.h @@ -0,0 +1,43 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _NET_SOCKETS_BSD_H_ +#define _NET_SOCKETS_BSD_H_ + +#include +#include +#include +#include +#include + +typedef int SOCKET_T; +typedef socklen_t SOCKADDR_LEN_T; +typedef void *SOCKOPT_CAST_T; +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 + +#endif /* _NET_SOCKETS_BSD_H_ */ diff --git a/containers/net/net_sockets_common.c b/containers/net/net_sockets_common.c new file mode 100755 index 0000000..b3815f3 --- /dev/null +++ b/containers/net/net_sockets_common.c @@ -0,0 +1,619 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "net_sockets.h" +#include "net_sockets_priv.h" + +/*****************************************************************************/ + +struct vc_container_net_tag +{ + /** The underlying socket */ + SOCKET_T socket; + /** Last error raised on the socket instance. */ + vc_container_net_status_t status; + /** Simple socket type */ + vc_container_net_type_t type; + /** Socket address, used for sending datagrams. */ + union { + struct sockaddr_storage storage; + struct sockaddr sa; + struct sockaddr_in in; + struct sockaddr_in6 in6; + } to_addr; + /** Number of bytes in to_addr that have been filled. */ + SOCKADDR_LEN_T to_addr_len; + /** Maximum size of datagrams. */ + size_t max_datagram_size; + /** Timeout to use when reading from a socket. INFINITE_TIMEOUT_MS waits forever. */ + uint32_t read_timeout_ms; +}; + +/*****************************************************************************/ +static void socket_clear_address(struct sockaddr *p_addr) +{ + switch (p_addr->sa_family) + { + case AF_INET: + { + struct sockaddr_in *p_addr_v4 = (struct sockaddr_in *)p_addr; + + memset(&p_addr_v4->sin_addr, 0, sizeof(p_addr_v4->sin_addr)); + } + break; + case AF_INET6: + { + struct sockaddr_in6 *p_addr_v6 = (struct sockaddr_in6 *)p_addr; + + memset(&p_addr_v6->sin6_addr, 0, sizeof(p_addr_v6->sin6_addr)); + } + break; + default: + /* Invalid or unsupported address family */ + vc_container_assert(0); + } +} + +/*****************************************************************************/ +static vc_container_net_status_t socket_set_read_buffer_size(VC_CONTAINER_NET_T *p_ctx, + uint32_t buffer_size) +{ + int result; + const SOCKOPT_CAST_T optptr = (const SOCKOPT_CAST_T)&buffer_size; + + result = setsockopt(p_ctx->socket, SOL_SOCKET, SO_RCVBUF, optptr, sizeof(buffer_size)); + + if (result == SOCKET_ERROR) + return vc_container_net_private_last_error(); + + return VC_CONTAINER_NET_SUCCESS; +} + +/*****************************************************************************/ +static vc_container_net_status_t socket_set_read_timeout_ms(VC_CONTAINER_NET_T *p_ctx, + uint32_t timeout_ms) +{ + p_ctx->read_timeout_ms = timeout_ms; + return VC_CONTAINER_NET_SUCCESS; +} + +/*****************************************************************************/ +static bool socket_wait_for_data( VC_CONTAINER_NET_T *p_ctx, uint32_t timeout_ms ) +{ + int result; + fd_set set; + struct timeval tv; + + if (timeout_ms == INFINITE_TIMEOUT_MS) + return true; + + FD_ZERO(&set); + FD_SET(p_ctx->socket, &set); + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms - tv.tv_sec * 1000) * 1000; + result = select(p_ctx->socket + 1, &set, NULL, NULL, &tv); + + if (result == SOCKET_ERROR) + p_ctx->status = vc_container_net_private_last_error(); + else + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + + return (result == 1); +} + +/*****************************************************************************/ +VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port, + vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status ) +{ + VC_CONTAINER_NET_T *p_ctx; + struct addrinfo hints, *info, *p; + int result; + vc_container_net_status_t status; + SOCKET_T sock = INVALID_SOCKET; + + status = vc_container_net_private_init(); + if (status != VC_CONTAINER_NET_SUCCESS) + { + LOG_ERROR(NULL, "vc_container_net_open: platform initialization failure: %d", status); + if (p_status) + *p_status = status; + return NULL; + } + + p_ctx = (VC_CONTAINER_NET_T *)malloc(sizeof(VC_CONTAINER_NET_T)); + if (!p_ctx) + { + if (p_status) + *p_status = VC_CONTAINER_NET_ERROR_NO_MEMORY; + + LOG_ERROR(NULL, "vc_container_net_open: malloc fail for VC_CONTAINER_NET_T"); + vc_container_net_private_deinit(); + return NULL; + } + + /* Initialize the net socket instance structure */ + memset(p_ctx, 0, sizeof(*p_ctx)); + p_ctx->socket = INVALID_SOCKET; + if (flags & VC_CONTAINER_NET_OPEN_FLAG_STREAM) + p_ctx->type = address ? STREAM_CLIENT : STREAM_SERVER; + else + p_ctx->type = address ? DATAGRAM_SENDER : DATAGRAM_RECEIVER; + + /* Create the address info linked list from the data provided */ + memset(&hints, 0, sizeof(hints)); + switch (flags & VC_CONTAINER_NET_OPEN_FLAG_FORCE_MASK) + { + case 0: + hints.ai_family = AF_UNSPEC; + break; + case VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4: + hints.ai_family = AF_INET; + break; + case VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6: + hints.ai_family = AF_INET6; + break; + default: + status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + LOG_ERROR(NULL, "vc_container_net_open: invalid address forcing flag"); + goto error; + } + hints.ai_socktype = (flags & VC_CONTAINER_NET_OPEN_FLAG_STREAM) ? SOCK_STREAM : SOCK_DGRAM; + + result = getaddrinfo(address, port, &hints, &info); + if (result) + { + status = vc_container_net_private_last_error(); + LOG_ERROR(NULL, "vc_container_net_open: unable to get address info: %d", status); + goto error; + } + + /* Not all address infos may be useable. Search for one that is by skipping any + * that provoke errors. */ + for(p = info; (p != NULL) && (sock == INVALID_SOCKET) ; p = p->ai_next) + { + sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (sock == INVALID_SOCKET) + { + status = vc_container_net_private_last_error(); + continue; + } + + switch (p_ctx->type) + { + case STREAM_CLIENT: + /* Simply connect to the given address/port */ + if (connect(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR) + status = vc_container_net_private_last_error(); + break; + + case DATAGRAM_SENDER: + /* Nothing further to do */ + break; + + case STREAM_SERVER: + /* Try to avoid socket reuse timing issues on TCP server sockets */ + vc_container_net_private_set_reusable(sock, true); + + /* Allow any source address */ + socket_clear_address(p->ai_addr); + + if (bind(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR) + status = vc_container_net_private_last_error(); + break; + + case DATAGRAM_RECEIVER: + /* Allow any source address */ + socket_clear_address(p->ai_addr); + + if (bind(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR) + status = vc_container_net_private_last_error(); + break; + } + + if (status == VC_CONTAINER_NET_SUCCESS) + { + /* Save addressing information for later use */ + p_ctx->to_addr_len = p->ai_addrlen; + memcpy(&p_ctx->to_addr, p->ai_addr, p->ai_addrlen); + } else { + vc_container_net_private_close(sock); /* Try next entry in list */ + sock = INVALID_SOCKET; + } + } + + freeaddrinfo(info); + + if (sock == INVALID_SOCKET) + { + LOG_ERROR(NULL, "vc_container_net_open: failed to open socket: %d", status); + goto error; + } + + p_ctx->socket = sock; + p_ctx->max_datagram_size = vc_container_net_private_maximum_datagram_size(sock); + p_ctx->read_timeout_ms = INFINITE_TIMEOUT_MS; + + if (p_status) + *p_status = VC_CONTAINER_NET_SUCCESS; + + return p_ctx; + +error: + if (p_status) + *p_status = status; + (void)vc_container_net_close(p_ctx); + return NULL; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx ) +{ + if (!p_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + if (p_ctx->socket != INVALID_SOCKET) + { + vc_container_net_private_close(p_ctx->socket); + p_ctx->socket = INVALID_SOCKET; + } + free(p_ctx); + + vc_container_net_private_deinit(); + + return VC_CONTAINER_NET_SUCCESS; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx ) +{ + if (!p_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + return p_ctx->status; +} + +/*****************************************************************************/ +size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size ) +{ + int result = 0; + + if (!p_ctx) + return 0; + + if (!buffer) + { + p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + return 0; + } + + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + + switch (p_ctx->type) + { + case STREAM_CLIENT: + case STREAM_SERVER: + /* Receive data from the stream */ + if (socket_wait_for_data(p_ctx, p_ctx->read_timeout_ms)) + { + result = recv(p_ctx->socket, buffer, (int)size, 0); + if (!result) + p_ctx->status = VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + } else + p_ctx->status = VC_CONTAINER_NET_ERROR_TIMED_OUT; + break; + + case DATAGRAM_RECEIVER: + { + /* Receive the packet */ + /* FIXME Potential for data loss, as rest of packet will be lost if buffer was not large enough */ + if (socket_wait_for_data(p_ctx, p_ctx->read_timeout_ms)) + { + result = recvfrom(p_ctx->socket, buffer, size, 0, &p_ctx->to_addr.sa, &p_ctx->to_addr_len); + if (!result) + p_ctx->status = VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + } else + p_ctx->status = VC_CONTAINER_NET_ERROR_TIMED_OUT; + } + break; + + default: /* DATAGRAM_SENDER */ + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + result = 0; + break; + } + + if (result == SOCKET_ERROR) + { + p_ctx->status = vc_container_net_private_last_error(); + result = 0; + } + + return (size_t)result; +} + +/*****************************************************************************/ +size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size ) +{ + int result; + + if (!p_ctx) + return 0; + + if (!buffer) + { + p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + return 0; + } + + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + + switch (p_ctx->type) + { + case STREAM_CLIENT: + case STREAM_SERVER: + /* Send data to the stream */ + result = send(p_ctx->socket, buffer, (int)size, 0); + break; + + case DATAGRAM_SENDER: + /* Send the datagram */ + + if (size > p_ctx->max_datagram_size) + size = p_ctx->max_datagram_size; + + result = sendto(p_ctx->socket, buffer, size, 0, &p_ctx->to_addr.sa, p_ctx->to_addr_len); + break; + + default: /* DATAGRAM_RECEIVER */ + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + result = 0; + break; + } + + if (result == SOCKET_ERROR) + { + p_ctx->status = vc_container_net_private_last_error(); + result = 0; + } + + return (size_t)result; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections ) +{ + if (!p_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + + if (p_ctx->type == STREAM_SERVER) + { + if (listen(p_ctx->socket, maximum_connections) == SOCKET_ERROR) + p_ctx->status = vc_container_net_private_last_error(); + } else { + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + } + + return p_ctx->status; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx ) +{ + VC_CONTAINER_NET_T *p_client_ctx = NULL; + + if (!p_server_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + if (!pp_client_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + + *pp_client_ctx = NULL; + + if (p_server_ctx->type != STREAM_SERVER) + { + p_server_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + goto error; + } + + p_client_ctx = (VC_CONTAINER_NET_T *)malloc(sizeof(VC_CONTAINER_NET_T)); + if (!p_client_ctx) + { + p_server_ctx->status = VC_CONTAINER_NET_ERROR_NO_MEMORY; + goto error; + } + + /* Initialise the new context with the address information from the server context */ + memset(p_client_ctx, 0, sizeof(*p_client_ctx)); + memcpy(&p_client_ctx->to_addr, &p_server_ctx->to_addr, p_server_ctx->to_addr_len); + p_client_ctx->to_addr_len = p_server_ctx->to_addr_len; + + p_client_ctx->socket = accept(p_server_ctx->socket, &p_client_ctx->to_addr.sa, &p_client_ctx->to_addr_len); + + if (p_client_ctx->socket == INVALID_SOCKET) + { + p_server_ctx->status = vc_container_net_private_last_error(); + goto error; + } + + /* Need to bump up the initialisation count, as a new context has been created */ + p_server_ctx->status = vc_container_net_private_init(); + if (p_server_ctx->status != VC_CONTAINER_NET_SUCCESS) + goto error; + + p_client_ctx->type = STREAM_CLIENT; + p_client_ctx->max_datagram_size = vc_container_net_private_maximum_datagram_size(p_client_ctx->socket); + p_client_ctx->read_timeout_ms = INFINITE_TIMEOUT_MS; + p_client_ctx->status = VC_CONTAINER_NET_SUCCESS; + + *pp_client_ctx = p_client_ctx; + return VC_CONTAINER_NET_SUCCESS; + +error: + if (p_client_ctx) + free(p_client_ctx); + return p_server_ctx->status; +} + +/*****************************************************************************/ +bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx ) +{ + if (!p_ctx) + return false; + + if (p_ctx->type == DATAGRAM_SENDER) + { + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + return false; + } + + return socket_wait_for_data(p_ctx, 0); +} + +/*****************************************************************************/ +size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx ) +{ + return p_ctx ? p_ctx->max_datagram_size : 0; +} + +/*****************************************************************************/ +static vc_container_net_status_t translate_getnameinfo_error( int error ) +{ + switch (error) + { + case EAI_AGAIN: return VC_CONTAINER_NET_ERROR_TRY_AGAIN; + case EAI_FAIL: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; + case EAI_MEMORY: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case EAI_NONAME: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; + + /* All other errors are unexpected, so just map to a general purpose error code. */ + default: + return VC_CONTAINER_NET_ERROR_GENERAL; + } +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len ) +{ + int result; + + if (!p_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + if (p_ctx->socket == INVALID_SOCKET) + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_CONNECTED; + else if (!name || !name_len) + p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + else if ((result = getnameinfo(&p_ctx->to_addr.sa, p_ctx->to_addr_len, name, name_len, NULL, 0, 0)) != 0) + p_ctx->status = translate_getnameinfo_error(result); + else + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + + return p_ctx->status; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port ) +{ + if (!p_ctx) + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + + if (p_ctx->socket == INVALID_SOCKET) + p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_CONNECTED; + else if (!port) + p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + else + { + p_ctx->status = VC_CONTAINER_NET_SUCCESS; + switch (p_ctx->to_addr.sa.sa_family) + { + case AF_INET: + *port = ntohs(p_ctx->to_addr.in.sin_port); + break; + case AF_INET6: + *port = ntohs(p_ctx->to_addr.in6.sin6_port); + break; + default: + /* Highly unexepcted address family! */ + p_ctx->status = VC_CONTAINER_NET_ERROR_GENERAL; + } + } + + return p_ctx->status; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx, + vc_container_net_control_t operation, + va_list args) +{ + vc_container_net_status_t status; + + switch (operation) + { + case VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE: + status = socket_set_read_buffer_size(p_ctx, va_arg(args, uint32_t)); + break; + case VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS: + status = socket_set_read_timeout_ms(p_ctx, va_arg(args, uint32_t)); + break; + default: + status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + } + + return status; +} + +/*****************************************************************************/ +uint32_t vc_container_net_to_host( uint32_t value ) +{ + return ntohl(value); +} + +/*****************************************************************************/ +uint32_t vc_container_net_from_host( uint32_t value ) +{ + return htonl(value); +} + +/*****************************************************************************/ +uint16_t vc_container_net_to_host_16( uint16_t value ) +{ + return ntohs(value); +} + +/*****************************************************************************/ +uint16_t vc_container_net_from_host_16( uint16_t value ) +{ + return htons(value); +} diff --git a/containers/net/net_sockets_null.c b/containers/net/net_sockets_null.c new file mode 100755 index 0000000..a8fe8fb --- /dev/null +++ b/containers/net/net_sockets_null.c @@ -0,0 +1,175 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "net_sockets.h" +#include "containers/core/containers_common.h" + +/*****************************************************************************/ + +struct vc_container_net_tag +{ + uint32_t dummy; /* C requires structs not to be empty. */ +}; + +/*****************************************************************************/ +VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port, + vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status ) +{ + VC_CONTAINER_PARAM_UNUSED(address); + VC_CONTAINER_PARAM_UNUSED(port); + VC_CONTAINER_PARAM_UNUSED(flags); + + if (p_status) + *p_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED; + + return NULL; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(buffer); + VC_CONTAINER_PARAM_UNUSED(size); + + return 0; +} + +/*****************************************************************************/ +size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(buffer); + VC_CONTAINER_PARAM_UNUSED(size); + + return 0; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(maximum_connections); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_server_ctx); + VC_CONTAINER_PARAM_UNUSED(pp_client_ctx); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + return false; +} + +/*****************************************************************************/ +size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + return 0; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(name); + VC_CONTAINER_PARAM_UNUSED(name_len); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port ) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(port); + + return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx, + vc_container_net_control_t operation, + va_list args) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(operation); + VC_CONTAINER_PARAM_UNUSED(args); + + return VC_CONTAINER_NET_ERROR_NOT_ALLOWED; +} + +/*****************************************************************************/ +uint32_t vc_container_net_to_host( uint32_t value ) +{ + return value; +} + +/*****************************************************************************/ +uint32_t vc_container_net_from_host( uint32_t value ) +{ + return value; +} + +/*****************************************************************************/ +uint16_t vc_container_net_to_host_16( uint16_t value ) +{ + return value; +} + +/*****************************************************************************/ +uint16_t vc_container_net_from_host_16( uint16_t value ) +{ + return value; +} diff --git a/containers/net/net_sockets_priv.h b/containers/net/net_sockets_priv.h new file mode 100755 index 0000000..4186083 --- /dev/null +++ b/containers/net/net_sockets_priv.h @@ -0,0 +1,85 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _NET_SOCKETS_PRIV_H_ +#define _NET_SOCKETS_PRIV_H_ + +#include "net_sockets.h" + +#ifdef WIN32 +#include "net_sockets_win32.h" +#else +#include "net_sockets_bsd.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + STREAM_CLIENT = 0, /**< TCP client */ + STREAM_SERVER, /**< TCP server */ + DATAGRAM_SENDER, /**< UDP sender */ + DATAGRAM_RECEIVER /**< UDP receiver */ +} vc_container_net_type_t; + + +/** Perform implementation-specific per-socket initialization. + * + * \return VC_CONTAINER_NET_SUCCESS or one of the error codes on failure. */ +vc_container_net_status_t vc_container_net_private_init( void ); + +/** Perform implementation-specific per-socket deinitialization. + * This function is always called once for each successful call to socket_private_init(). */ +void vc_container_net_private_deinit( void ); + +/** Return the last error from the socket implementation. */ +vc_container_net_status_t vc_container_net_private_last_error( void ); + +/** Implementation-specific internal socket close. + * + * \param sock Internal socket to be closed. */ +void vc_container_net_private_close( SOCKET_T sock ); + +/** Enable or disable socket address reusability. + * + * \param sock Internal socket to be closed. + * \param enable True to enable reusability, false to clear it. */ +void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable ); + +/** Query the maximum datagram size for the socket. + * + * \param sock The socket to query. + * \return The maximum supported datagram size on the socket. */ +size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock ); + +#ifdef __cplusplus +} +#endif + +#endif /* _NET_SOCKETS_PRIV_H_ */ diff --git a/containers/net/net_sockets_win32.c b/containers/net/net_sockets_win32.c new file mode 100755 index 0000000..e0dd374 --- /dev/null +++ b/containers/net/net_sockets_win32.c @@ -0,0 +1,140 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "net_sockets.h" +#include "net_sockets_priv.h" +#include "containers/core/containers_common.h" + +#pragma comment(lib, "Ws2_32.lib") + +/*****************************************************************************/ + +/** Default maximum datagram size. + * This is based on the default Ethernet MTU size, less the IP and UDP headers. + */ +#define DEFAULT_MAXIMUM_DATAGRAM_SIZE (1500 - 20 - 8) + +/** Maximum socket buffer size to use. */ +#define MAXIMUM_BUFFER_SIZE 65536 + +/*****************************************************************************/ +static vc_container_net_status_t translate_error_status( int error ) +{ + switch (error) + { + case WSA_INVALID_HANDLE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + case WSA_NOT_ENOUGH_MEMORY: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case WSA_INVALID_PARAMETER: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAEACCES: return VC_CONTAINER_NET_ERROR_ACCESS_DENIED; + case WSAEFAULT: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAEINVAL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAEMFILE: return VC_CONTAINER_NET_ERROR_TOO_BIG; + case WSAEWOULDBLOCK: return VC_CONTAINER_NET_ERROR_WOULD_BLOCK; + case WSAEINPROGRESS: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; + case WSAEALREADY: return VC_CONTAINER_NET_ERROR_IN_PROGRESS; + case WSAEADDRINUSE: return VC_CONTAINER_NET_ERROR_IN_USE; + case WSAEADDRNOTAVAIL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAENETDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; + case WSAENETUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; + case WSAENETRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case WSAECONNABORTED: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case WSAECONNRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case WSAENOBUFS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case WSAENOTCONN: return VC_CONTAINER_NET_ERROR_NOT_CONNECTED; + case WSAESHUTDOWN: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case WSAETIMEDOUT: return VC_CONTAINER_NET_ERROR_TIMED_OUT; + case WSAECONNREFUSED: return VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED; + case WSAELOOP: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAENAMETOOLONG: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER; + case WSAEHOSTDOWN: return VC_CONTAINER_NET_ERROR_NETWORK; + case WSAEHOSTUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK; + case WSAEPROCLIM: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case WSAEUSERS: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case WSAEDQUOT: return VC_CONTAINER_NET_ERROR_NO_MEMORY; + case WSAESTALE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET; + case WSAEDISCON: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST; + case WSAHOST_NOT_FOUND: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; + case WSATRY_AGAIN: return VC_CONTAINER_NET_ERROR_TRY_AGAIN; + case WSANO_RECOVERY: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; + case WSANO_DATA: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND; + + /* All other errors are unexpected, so just map to a general purpose error code. */ + default: + return VC_CONTAINER_NET_ERROR_GENERAL; + } +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_private_last_error() +{ + return translate_error_status( WSAGetLastError() ); +} + +/*****************************************************************************/ +vc_container_net_status_t vc_container_net_private_init() +{ + WSADATA wsa_data; + int result; + + result = WSAStartup(MAKEWORD(2,2), &wsa_data); + if (result) + return translate_error_status( result ); + + return VC_CONTAINER_NET_SUCCESS; +} + +/*****************************************************************************/ +void vc_container_net_private_deinit() +{ + WSACleanup(); +} + +/*****************************************************************************/ +void vc_container_net_private_close( SOCKET_T sock ) +{ + closesocket(sock); +} + +/*****************************************************************************/ +void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable ) +{ + BOOL opt = enable ? TRUE : FALSE; + + (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); +} + +/*****************************************************************************/ +size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock ) +{ + size_t max_datagram_size = DEFAULT_MAXIMUM_DATAGRAM_SIZE; + int opt_size = sizeof(max_datagram_size); + + /* Ignore errors and use the default if necessary */ + (void)getsockopt(sock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *)&max_datagram_size, &opt_size); + + return max_datagram_size; +} diff --git a/containers/net/net_sockets_win32.h b/containers/net/net_sockets_win32.h new file mode 100755 index 0000000..86edc3a --- /dev/null +++ b/containers/net/net_sockets_win32.h @@ -0,0 +1,38 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _NET_SOCKETS_WIN32_H_ +#define _NET_SOCKETS_WIN32_H_ + +#include +#include + +typedef SOCKET SOCKET_T; +typedef int SOCKADDR_LEN_T; +typedef char *SOCKOPT_CAST_T; + +#endif /* _NET_SOCKETS_WIN32_H_ */ diff --git a/containers/packetizers.h b/containers/packetizers.h new file mode 100755 index 0000000..735816d --- /dev/null +++ b/containers/packetizers.h @@ -0,0 +1,159 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef VC_PACKETIZERS_H +#define VC_PACKETIZERS_H + +/** \file packetizers.h + * Public API for packetizing data (i.e. framing and timestamping) + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "containers/containers.h" + +/** \defgroup VcPacketizerApi Packetizer API + * API for packetizers */ +/* @{ */ + +/** \name Packetizer flags + * \anchor packetizerflags + * The following flags describe properties of a packetizer */ +/* @{ */ +#define VC_PACKETIZER_FLAG_ES_CHANGED 0x1 /**< ES definition has changed */ +/* @} */ + +/** Definition of the packetizer type */ +typedef struct VC_PACKETIZER_T +{ + struct VC_PACKETIZER_PRIVATE_T *priv; /**< Private member used by the implementation */ + uint32_t flags; /**< Flags describing the properties of a packetizer. + * See \ref packetizerflags "Packetizer flags". */ + + VC_CONTAINER_ES_FORMAT_T *in; /**< Format of the input elementary stream */ + VC_CONTAINER_ES_FORMAT_T *out; /**< Format of the output elementary stream */ + + uint32_t max_frame_size; /**< Maximum size of a packetized frame */ + +} VC_PACKETIZER_T; + +/** Open a packetizer to convert the input format into the requested output format. + * This will create an an instance of a packetizer and its associated context. + * + * If no packetizer is found for the requested format, this will return a null pointer as well as + * an error code indicating why this failed. + * + * \param in Input elementary stream format + * \param out_variant Requested output variant for the output elementary stream format + * \param status Returns the status of the operation + * \return A pointer to the context of the new instance of the packetizer. + * Returns NULL on failure. + */ +VC_PACKETIZER_T *vc_packetizer_open(VC_CONTAINER_ES_FORMAT_T *in, VC_CONTAINER_FOURCC_T out_variant, + VC_CONTAINER_STATUS_T *status); + +/** Closes an instance of a packetizer. + * This will free all the resources associated with the context. + * + * \param context Pointer to the context of the instance to close + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_packetizer_close( VC_PACKETIZER_T *context ); + +/** \name Packetizer flags + * The following flags can be passed during a packetize call */ +/* @{ */ +/** Type definition for the packetizer flags */ +typedef uint32_t VC_PACKETIZER_FLAGS_T; +/** Ask the packetizer to only return information on the next packet without reading it */ +#define VC_PACKETIZER_FLAG_INFO 0x1 +/** Ask the packetizer to skip the next packet */ +#define VC_PACKETIZER_FLAG_SKIP 0x2 +/** Ask the packetizer to flush any data being processed */ +#define VC_PACKETIZER_FLAG_FLUSH 0x4 +/** Force the packetizer to release an input packet */ +#define VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT 0x8 +/* @} */ + +/** Push a new packet of data to the packetizer. + * This is the mechanism used to feed data into the packetizer. Once a packet has been + * pushed into the packetizer it is owned by the packetizer until released by a call to + * \ref vc_packetizer_pop + * + * \param context Pointer to the context of the packetizer to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * to push into the packetizer. + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_packetizer_push( VC_PACKETIZER_T *context, + VC_CONTAINER_PACKET_T *packet); + +/** Pop a packet of data from the packetizer. + * This allows the client to retrieve consumed data from the packetizer. Packets returned by + * the packetizer in this manner can then be released / recycled by the client. + * It is possible for the client to retrieve non-consumed data by passing the + * VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT flag. This will however trigger some internal buffering + * inside the packetizer and thus is less efficient. + * + * \param context Pointer to the context of the packetizer to use + * \param packet Pointer used to return a consumed packet + * \param flags Miscellaneous flags controlling the operation + * + * \return VC_CONTAINER_SUCCESS if a consumed packet was retrieved, + * VC_CONTAINER_ERROR_INCOMPLETE_DATA if none is available. + */ +VC_CONTAINER_STATUS_T vc_packetizer_pop( VC_PACKETIZER_T *context, + VC_CONTAINER_PACKET_T **packet, VC_PACKETIZER_FLAGS_T flags); + +/** Read packetized data out of the packetizer. + * + * \param context Pointer to the context of the packetizer to use + * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet + * This might need to be partially filled before the call (buffer, buffer_size) + * depending on the flags used. + * \param flags Miscellaneous flags controlling the operation + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_packetizer_read( VC_PACKETIZER_T *context, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags); + +/** Reset packetizer state. + * This will reset the state of the packetizer as well as mark all data pushed to it as consumed. + * + * \param context Pointer to the context of the packetizer to reset + * \return the status of the operation + */ +VC_CONTAINER_STATUS_T vc_packetizer_reset( VC_PACKETIZER_T *context ); + +/* @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* VC_PACKETIZERS_H */ diff --git a/containers/pcm/pcm_packetizer.c b/containers/pcm/pcm_packetizer.c new file mode 100755 index 0000000..8dea398 --- /dev/null +++ b/containers/pcm/pcm_packetizer.c @@ -0,0 +1,269 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Implementation of a PCM packetizer. + */ + +#include +#include + +#include "containers/packetizers.h" +#include "containers/core/packetizers_private.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_time.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_bytestream.h" + +#define FRAME_SIZE (16*1024) /**< Arbitrary value which is neither too small nor too big */ +#define FACTOR_SHIFT 4 /**< Shift applied to the conversion factor */ + +VC_CONTAINER_STATUS_T pcm_packetizer_open( VC_PACKETIZER_T * ); + +/*****************************************************************************/ +enum conversion { + CONVERSION_NONE = 0, + CONVERSION_U8_TO_S16L, + CONVERSION_UNKNOWN +}; + +typedef struct VC_PACKETIZER_MODULE_T { + enum { + STATE_NEW_PACKET = 0, + STATE_DATA + } state; + + unsigned int samples_per_frame; + unsigned int bytes_per_sample; + unsigned int max_frame_size; + + uint32_t bytes_read; + unsigned int frame_size; + + enum conversion conversion; + unsigned int conversion_factor; +} VC_PACKETIZER_MODULE_T; + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T pcm_packetizer_close( VC_PACKETIZER_T *p_ctx ) +{ + free(p_ctx->priv->module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T pcm_packetizer_reset( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + module->state = STATE_NEW_PACKET; + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static void convert_pcm_u8_to_s16l( uint8_t **p_out, uint8_t *in, size_t size) +{ + int16_t *out = (int16_t *)*p_out; + uint8_t tmp; + + while(size--) + { + tmp = *in++; + *out++ = ((tmp - 128) << 8) | tmp; + } + *p_out = (uint8_t *)out; +} + +/*****************************************************************************/ +static void convert_pcm( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_BYTESTREAM_T *stream, size_t size, uint8_t *out ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + uint8_t tmp[256]; + size_t tmp_size; + + while(size) + { + tmp_size = MIN(sizeof(tmp), size); + bytestream_get(stream, tmp, tmp_size); + if (module->conversion == CONVERSION_U8_TO_S16L) + convert_pcm_u8_to_s16l(&out, tmp, tmp_size); + else + bytestream_skip(stream, tmp_size); + size -= tmp_size; + } +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T pcm_packetizer_packetize( VC_PACKETIZER_T *p_ctx, + VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags ) +{ + VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream; + VC_CONTAINER_TIME_T *time = &p_ctx->priv->time; + int64_t pts, dts; + size_t offset, size; + + while(1) switch (module->state) + { + case STATE_NEW_PACKET: + /* Make sure we've got enough data */ + if(bytestream_size(stream) < module->max_frame_size && + !(flags & VC_PACKETIZER_FLAG_FLUSH)) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + if(!bytestream_size(stream)) + return VC_CONTAINER_ERROR_INCOMPLETE_DATA; + + module->frame_size = bytestream_size(stream); + if(module->frame_size > module->max_frame_size) + module->frame_size = module->max_frame_size; + bytestream_get_timestamps_and_offset(stream, &pts, &dts, &offset, true); + vc_container_time_set(time, pts); + if(pts != VC_CONTAINER_TIME_UNKNOWN) + vc_container_time_add(time, offset / module->bytes_per_sample); + + module->bytes_read = 0; + module->state = STATE_DATA; + /* fall through to the next state */ + + case STATE_DATA: + size = module->frame_size - module->bytes_read; + out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN; + out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END; + out->size = (size * module->conversion_factor) >> FACTOR_SHIFT; + + if(!module->bytes_read) + { + out->pts = out->dts = vc_container_time_get(time); + out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + + if(flags & VC_PACKETIZER_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + if(flags & VC_PACKETIZER_FLAG_SKIP) + { + bytestream_skip( stream, size ); + } + else + { + out->size = MIN(out->size, out->buffer_size); + size = (out->size << FACTOR_SHIFT) / module->conversion_factor; + out->size = (size * module->conversion_factor) >> FACTOR_SHIFT; + + if(module->conversion != CONVERSION_NONE) + convert_pcm(p_ctx, stream, size, out->data); + else + bytestream_get(stream, out->data, out->size); + } + module->bytes_read += size; + + if(module->bytes_read == module->frame_size) + { + vc_container_time_add(time, module->samples_per_frame); + module->state = STATE_NEW_PACKET; + } + return VC_CONTAINER_SUCCESS; + + default: + break; + }; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T pcm_packetizer_open( VC_PACKETIZER_T *p_ctx ) +{ + VC_PACKETIZER_MODULE_T *module; + unsigned int bytes_per_sample = 0; + enum conversion conversion = CONVERSION_NONE; + + if(p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_UNSIGNED_BE && + p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_UNSIGNED_LE && + p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_SIGNED_BE && + p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_SIGNED_LE && + p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_FLOAT_BE && + p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_FLOAT_LE) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if(p_ctx->in->type->audio.block_align) + bytes_per_sample = p_ctx->in->type->audio.block_align; + else if(p_ctx->in->type->audio.bits_per_sample && p_ctx->in->type->audio.channels) + bytes_per_sample = p_ctx->in->type->audio.bits_per_sample * + p_ctx->in->type->audio.channels / 8; + + if(!bytes_per_sample) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Check if we support any potential conversion we've been asked to do */ + if(p_ctx->out->codec_variant) + conversion = CONVERSION_UNKNOWN; + if(p_ctx->out->codec_variant == VC_FOURCC('s','1','6','l') && + p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_SIGNED_LE && + p_ctx->in->type->audio.bits_per_sample == 16) + conversion = CONVERSION_NONE; + if(p_ctx->out->codec_variant == VC_FOURCC('s','1','6','l') && + (p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_UNSIGNED_LE || + p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_UNSIGNED_BE) && + p_ctx->in->type->audio.bits_per_sample == 8) + conversion = CONVERSION_U8_TO_S16L; + if(conversion == CONVERSION_UNKNOWN) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + p_ctx->priv->module = module = malloc(sizeof(*module)); + if(!module) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + module->conversion = conversion; + module->conversion_factor = 1 << FACTOR_SHIFT; + + p_ctx->out->codec_variant = 0; + if(conversion == CONVERSION_U8_TO_S16L) + { + module->conversion_factor = 2 << FACTOR_SHIFT; + p_ctx->out->type->audio.bits_per_sample *= 2; + p_ctx->out->type->audio.block_align *= 2; + p_ctx->out->codec = VC_CONTAINER_CODEC_PCM_SIGNED_LE; + } + + vc_container_time_set_samplerate(&p_ctx->priv->time, p_ctx->in->type->audio.sample_rate, 1); + + p_ctx->max_frame_size = FRAME_SIZE; + module->max_frame_size = (FRAME_SIZE << FACTOR_SHIFT) / module->conversion_factor; + module->bytes_per_sample = bytes_per_sample; + module->samples_per_frame = module->max_frame_size / bytes_per_sample; + p_ctx->priv->pf_close = pcm_packetizer_close; + p_ctx->priv->pf_packetize = pcm_packetizer_packetize; + p_ctx->priv->pf_reset = pcm_packetizer_reset; + + LOG_DEBUG(0, "using pcm audio packetizer"); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_PACKETIZER_REGISTER(pcm_packetizer_open, "pcm"); diff --git a/containers/qsynth/CMakeLists.txt b/containers/qsynth/CMakeLists.txt new file mode 100755 index 0000000..b188e50 --- /dev/null +++ b/containers/qsynth/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_qsynth ${LIBRARY_TYPE} qsynth_reader.c) + +target_link_libraries(reader_qsynth containers) + +install(TARGETS reader_qsynth DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/qsynth/qsynth_reader.c b/containers/qsynth/qsynth_reader.c new file mode 100755 index 0000000..06a9d54 --- /dev/null +++ b/containers/qsynth/qsynth_reader.c @@ -0,0 +1,482 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ + +#define BI32(b) (((b)[0]<<24)|((b)[1]<<16)|((b)[2]<<8)|((b)[3])) +#define BI16(b) (((b)[0]<<8)|((b)[1])) + +#define HEADER_LENGTH 14 +#define MAX_TRACKS 128 + +/****************************************************************************** +Type definitions +******************************************************************************/ +struct _QSYNTH_SEGMENT_T { + struct _QSYNTH_SEGMENT_T *next; + uint32_t len; + uint8_t *data; +}; +typedef struct _QSYNTH_SEGMENT_T QSYNTH_SEGMENT_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + uint32_t filesize; + QSYNTH_SEGMENT_T *seg; + QSYNTH_SEGMENT_T *pass; + uint32_t sent; + int64_t timestamp; + uint32_t seek; +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +static VC_CONTAINER_STATUS_T qsynth_read_header(uint8_t *data, uint32_t *tracks, + uint32_t *division, uint8_t *fps, uint8_t *dpf) +{ + if(data[0] != 'M' || data[1] != 'T' || data[2] != 'h' || data[3] != 'd' || + data[4] != 0 || data[5] != 0 || data[6] != 0 || data[7] != 6) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if(data[12] < 0x80) + { + if(division) *division = BI16(data+12); + } + else + { + if(fps) *fps = 256-data[12]; + if(dpf) *dpf = data[13]; + } + + if(tracks) *tracks = BI16(data+10); + + return VC_CONTAINER_SUCCESS; +} + +static int qsynth_read_variable(uint8_t *data, uint32_t *val) +{ + int i = 0; + *val = 0; + do { + *val = (*val << 7) + (data[i] & 0x7f); + } while(data[i++] & 0x80); + + return i; +} + +static VC_CONTAINER_STATUS_T qsynth_read_event(uint8_t *data, uint32_t *used, uint8_t *last, + uint32_t *time, uint32_t *tempo, uint32_t *end) +{ + int read; + + // need at least 4 bytes here + read = qsynth_read_variable(data, time); + + if(data[read] == 0xff) // meta event + { + uint32_t len; + uint8_t type = data[read+1]; + + read += 2; + read += qsynth_read_variable(data+read, &len); + + if(type == 0x2f) // end of track + { + if(len != 0) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + *end = 1; + } + else if(type == 0x51) // tempo event + { + if(len != 3) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + *tempo = (data[read]<<16) | (data[read+1]<<8) | data[read+2]; + } + + read += len; + } + else if(data[read] == 0xf0 || data[read] == 0xf7) // sysex events + { + uint32_t len; + read += 1; + read += qsynth_read_variable(data+read, &len) + len; + } + else // midi event + { + uint8_t type; + + if(data[read] < 128) + type = *last; + else + { + type = data[read] >> 4; + *last = type; + read++; + } + + switch(type) { + case 8: case 9: case 0xa: case 0xb: case 0xe: + read += 2; + break; + case 0xc: case 0xd: + read += 1; + break; + default: + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + } + + *used = read; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T qsynth_read_track(QSYNTH_SEGMENT_T *seg, + uint32_t *ticks, int64_t *time, + uint32_t *us_perclock, uint32_t *tempo_ticks) +{ + uint32_t total_ticks = 0; + uint32_t used = 8; + uint8_t last = 0; + + *time = 0LL; + *tempo_ticks = 0; + + while(used < seg->len) + { + VC_CONTAINER_STATUS_T status; + uint32_t event_ticks, new_tempo = 0, end = 0, event_used; + if((status = qsynth_read_event(seg->data+used, &event_used, &last, &event_ticks, &new_tempo, &end)) != VC_CONTAINER_SUCCESS) + return status; + + used += event_used; + total_ticks += event_ticks; + + if(new_tempo != 0) + { + *time += ((int64_t) (total_ticks - *tempo_ticks)) * (*us_perclock); + *us_perclock = new_tempo; + *tempo_ticks = total_ticks; + } + + if(end) + break; + } + + *ticks = total_ticks; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T qsynth_get_duration(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + QSYNTH_SEGMENT_T **seg = &(module->seg); + uint32_t i, tracks, division = 0, max_ticks = 0, us_perclock = 500000; + uint32_t end_uspc = 0, end_ticks = 0; + int64_t end_time = 0; + uint8_t fps = 1, dpf = 1; + + if((*seg = malloc(sizeof(QSYNTH_SEGMENT_T) + HEADER_LENGTH)) == NULL) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + (*seg)->next = NULL; + (*seg)->len = HEADER_LENGTH; + (*seg)->data = (uint8_t *) ((*seg) + 1); + + if(PEEK_BYTES(p_ctx, (*seg)->data, HEADER_LENGTH) != HEADER_LENGTH) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if((status = qsynth_read_header((*seg)->data, &tracks, &division, &fps, &dpf)) != VC_CONTAINER_SUCCESS) + return status; + + // if we have a suspiciously large number of tracks, this is probably a bad file + if(tracks > MAX_TRACKS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + SKIP_BYTES(p_ctx, HEADER_LENGTH); + + seg = &((*seg)->next); + module->filesize = HEADER_LENGTH; + + if(division == 0) + { + us_perclock = 1000000 / (fps * dpf); + division = 1; + } + + for(i=0; i (1<<20) || (*seg = malloc(sizeof(QSYNTH_SEGMENT_T) + 8 + len)) == NULL) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + module->filesize += len+8; + (*seg)->next = NULL; + (*seg)->len = len + 8; + (*seg)->data = (uint8_t *) ((*seg) + 1); + + memcpy((*seg)->data, dummy, 8); + if(READ_BYTES(p_ctx, (*seg)->data+8, len) != len) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + if((status = qsynth_read_track(*seg, &ticks, &time, &us_perclock, &tempo_ticks)) != VC_CONTAINER_SUCCESS) + return status; + + if(end_uspc == 0) + { + end_uspc = us_perclock; + end_ticks = tempo_ticks; + end_time = time; + } + + if(ticks > max_ticks) + max_ticks = ticks; + + seg = &((*seg)->next); + } + + if(end_uspc == 0) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + module->pass = module->seg; + module->sent = 0; + p_ctx->duration = (end_time + (((int64_t) (max_ticks - end_ticks)) * end_uspc)) / division; + module->track->format->extradata = (uint8_t *) &module->filesize; + module->track->format->extradata_size = 4; + return VC_CONTAINER_SUCCESS; +} + + +/***************************************************************************** +Functions exported as part of the Container Module API +*****************************************************************************/ +static VC_CONTAINER_STATUS_T qsynth_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, + uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + if(module->pass) + { + packet->size = module->pass->len - module->sent; + packet->dts = packet->pts = 0; + packet->track = 0; + packet->flags = module->sent ? 0 : VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + else + { + if(module->timestamp > p_ctx->duration) + return VC_CONTAINER_ERROR_EOS; + + packet->size = 5; + packet->dts = packet->pts = module->timestamp; + packet->track = 0; + packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME; + } + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + if(module->pass) + { + module->pass = module->pass->next; + module->sent = 0; + } + else + { + // if we're playing then we can't really skip, but have to simulate a seek instead + module->seek = 1; + module->timestamp += 40; + } + + return VC_CONTAINER_SUCCESS; + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + // read frame into packet->data + if(module->pass) + { + uint32_t copy = MIN(packet->size, packet->buffer_size); + memcpy(packet->data, module->pass->data + module->sent, copy); + packet->size = copy; + + if((module->sent += copy) == module->pass->len) + { + module->pass = module->pass->next; + module->sent = 0; + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + } + } + else + { + if(packet->buffer_size < packet->size) + return VC_CONTAINER_ERROR_BUFFER_TOO_SMALL; + + if(module->seek) + { + uint32_t current_time = module->timestamp / 1000; + + packet->data[0] = 'S'; + packet->data[1] = (uint8_t)((current_time >> 24) & 0xFF); + packet->data[2] = (uint8_t)((current_time >> 16) & 0xFF); + packet->data[3] = (uint8_t)((current_time >> 8) & 0xFF); + packet->data[4] = (uint8_t)((current_time ) & 0xFF); + module->seek = 0; + } + else + { + packet->data[0] = 'P'; + packet->data[1] = 0; + packet->data[2] = 0; + packet->data[3] = 0; + packet->data[4] = 40; + module->timestamp += 40 * 1000; + } + } + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T qsynth_reader_seek( VC_CONTAINER_T *p_ctx, + int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(flags); + + if (mode != VC_CONTAINER_SEEK_MODE_TIME) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + + if(*offset < 0) + *offset = 0; + else if(*offset > p_ctx->duration) + *offset = p_ctx->duration; + + module->timestamp = *offset; + module->seek = 1; + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T qsynth_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + QSYNTH_SEGMENT_T *seg = module->seg; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + while(seg != NULL) + { + QSYNTH_SEGMENT_T *next = seg->next; + free(seg); + seg = next; + } + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + uint8_t header[HEADER_LENGTH]; + + /* Check the file header */ + if((PEEK_BYTES(p_ctx, header, HEADER_LENGTH) != HEADER_LENGTH) || + qsynth_read_header(header, 0, 0, 0, 0) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks_num = 1; + p_ctx->tracks = &module->track; + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_MIDI; + p_ctx->tracks[0]->is_enabled = true; + + if((status = qsynth_get_duration(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; + + LOG_DEBUG(p_ctx, "using qsynth reader"); + + p_ctx->capabilities = VC_CONTAINER_CAPS_CAN_SEEK; + + p_ctx->priv->pf_close = qsynth_reader_close; + p_ctx->priv->pf_read = qsynth_reader_read; + p_ctx->priv->pf_seek = qsynth_reader_seek; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "qsynth: error opening stream (%i)", status); + if(module) qsynth_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function +********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open qsynth_reader_open +#endif diff --git a/containers/raw/CMakeLists.txt b/containers/raw/CMakeLists.txt new file mode 100755 index 0000000..0ada17e --- /dev/null +++ b/containers/raw/CMakeLists.txt @@ -0,0 +1,18 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_raw_video ${LIBRARY_TYPE} raw_video_reader.c) + +target_link_libraries(reader_raw_video containers) + +install(TARGETS reader_raw_video DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_raw_video ${LIBRARY_TYPE} raw_video_writer.c) + +target_link_libraries(writer_raw_video containers) + +install(TARGETS writer_raw_video DESTINATION ${VMCS_PLUGIN_DIR}) diff --git a/containers/raw/raw_video_common.h b/containers/raw/raw_video_common.h new file mode 100755 index 0000000..52d73ec --- /dev/null +++ b/containers/raw/raw_video_common.h @@ -0,0 +1,66 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef RAW_VIDEO_COMMON_H +#define RAW_VIDEO_COMMON_H + +static struct { + const char *id; + VC_CONTAINER_FOURCC_T codec; + unsigned int size_num; + unsigned int size_den; +} table[] = { + {"420", VC_CONTAINER_CODEC_I420, 3, 2}, + {0, 0, 0, 0} +}; + +STATIC_INLINE bool from_yuv4mpeg2(const char *id, VC_CONTAINER_FOURCC_T *codec, + unsigned int *size_num, unsigned int *size_den) +{ + unsigned int i; + for (i = 0; table[i].id; i++) + if (!strcmp(id, table[i].id)) + break; + if (codec) *codec = table[i].codec; + if (size_num) *size_num = table[i].size_num; + if (size_den) *size_den = table[i].size_den; + return !!table[i].id; +} + +STATIC_INLINE bool to_yuv4mpeg2(VC_CONTAINER_FOURCC_T codec, const char **id, + unsigned int *size_num, unsigned int *size_den) +{ + unsigned int i; + for (i = 0; table[i].id; i++) + if (codec == table[i].codec) + break; + if (id) *id = table[i].id; + if (size_num) *size_num = table[i].size_num; + if (size_den) *size_den = table[i].size_den; + return !!table[i].id; +} + +#endif /* RAW_VIDEO_COMMON_H */ diff --git a/containers/raw/raw_video_reader.c b/containers/raw/raw_video_reader.c new file mode 100755 index 0000000..00ac064 --- /dev/null +++ b/containers/raw/raw_video_reader.c @@ -0,0 +1,465 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +#include "raw_video_common.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define FILE_HEADER_SIZE_MAX 1024 +#define FRAME_HEADER_SIZE_MAX 256 +#define OPTION_SIZE_MAX 32 + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + VC_CONTAINER_STATUS_T status; + + bool yuv4mpeg2; + bool non_standard; + char option[OPTION_SIZE_MAX]; + + bool frame_header; + unsigned int frame_header_size; + + int64_t data_offset; + unsigned int block_size; + unsigned int block_offset; + unsigned int frames; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T read_yuv4mpeg2_option( VC_CONTAINER_T *ctx, + unsigned int *bytes_left ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int size, i; + + /* Start by skipping spaces */ + while (*bytes_left && PEEK_U8(ctx) == ' ') + (*bytes_left)--, _SKIP_U8(ctx); + + size = PEEK_BYTES(ctx, module->option, + MIN(sizeof(module->option), *bytes_left)); + + /* The config option ends at next space or newline */ + for (i = 0; i < size; i++) + { + if (module->option[i] == ' ' || module->option[i] == 0x0a) + { + module->option[i] = 0; + break; + } + } + if (i == 0) + return VC_CONTAINER_ERROR_NOT_FOUND; + + *bytes_left -= i; + SKIP_BYTES(ctx, i); + + /* If option is too long, we just discard it */ + if (i == size) + { + while (*bytes_left && PEEK_U8(ctx) != ' ' && PEEK_U8(ctx) != 0x0a) + (*bytes_left)--, _SKIP_U8(ctx); + return VC_CONTAINER_ERROR_NOT_FOUND; + } + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T read_yuv4mpeg2_file_header( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int bytes_left = FILE_HEADER_SIZE_MAX - 10; + unsigned int value1, value2; + char codec[OPTION_SIZE_MAX] = "420"; + uint8_t h[10]; + + /* Check for the YUV4MPEG2 signature */ + if (READ_BYTES(ctx, h, sizeof(h)) != sizeof(h)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if (memcmp(h, "YUV4MPEG2 ", sizeof(h))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Parse parameters */ + while (read_yuv4mpeg2_option(ctx, &bytes_left) == VC_CONTAINER_SUCCESS) + { + if (sscanf(module->option, "W%i", &value1) == 1) + ctx->tracks[0]->format->type->video.width = value1; + else if (sscanf(module->option, "H%i", &value1) == 1) + ctx->tracks[0]->format->type->video.height = value1; + else if (sscanf(module->option, "S%i", &value1) == 1) + module->block_size = value1; + else if (sscanf(module->option, "F%i:%i", &value1, &value2) == 2) + { + ctx->tracks[0]->format->type->video.frame_rate_num = value1; + ctx->tracks[0]->format->type->video.frame_rate_den = value2; + } + else if (sscanf(module->option, "A%i:%i", &value1, &value2) == 2) + { + ctx->tracks[0]->format->type->video.par_num = value1; + ctx->tracks[0]->format->type->video.par_den = value2; + } + else if (module->option[0] == 'C') + { + strcpy(codec, module->option+1); + } + } + + /* Check the end marker */ + if (_READ_U8(ctx) != 0x0a) + { + LOG_ERROR(ctx, "missing end of header marker"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + /* Find out which codec we are dealing with */ + if (from_yuv4mpeg2(codec, &ctx->tracks[0]->format->codec, &value1, &value2)) + { + module->block_size = ctx->tracks[0]->format->type->video.width * + ctx->tracks[0]->format->type->video.height * value1 / value2; + } + else + { + memcpy(&ctx->tracks[0]->format->codec, codec, 4); + module->non_standard = true; + } + + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T read_yuv4mpeg2_frame_header( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int bytes_left = FRAME_HEADER_SIZE_MAX - 5; + unsigned int value1; + char header[5]; + + if (READ_BYTES(ctx, header, sizeof(header)) != sizeof(header) || + memcmp(header, "FRAME", sizeof(header))) + { + LOG_ERROR(ctx, "missing frame marker"); + return STREAM_STATUS(ctx) != VC_CONTAINER_SUCCESS ? + STREAM_STATUS(ctx) : VC_CONTAINER_ERROR_CORRUPTED; + } + + /* Parse parameters */ + while (read_yuv4mpeg2_option(ctx, &bytes_left) == VC_CONTAINER_SUCCESS) + { + if (module->non_standard && sscanf(module->option, "S%i", &value1) == 1) + module->block_size = value1; + } + + /* Check the end marker */ + if (_READ_U8(ctx) != 0x0a) + { + LOG_ERROR(ctx, "missing end of frame header marker"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + module->frame_header_size = FRAME_HEADER_SIZE_MAX - bytes_left - 1; + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T rawvideo_parse_uri( VC_CONTAINER_T *ctx, + VC_CONTAINER_FOURCC_T *c, unsigned int *w, unsigned int *h, + unsigned int *fr_num, unsigned int *fr_den, unsigned *block_size ) +{ + VC_CONTAINER_FOURCC_T codec = 0; + unsigned int i, matches, width = 0, height = 0, fn = 0, fd = 0, size = 0; + const char *uri = ctx->priv->io->uri; + + /* Try and find a match for the string describing the format */ + for (i = 0; uri[i]; i++) + { + if (uri[i] != '_' && uri[i+1] != 'C') + continue; + + matches = sscanf(uri+i, "_C%4cW%iH%iF%i#%iS%i", (char *)&codec, + &width, &height, &fn, &fd, &size); + if (matches >= 3) + break; + } + if (!uri[i]) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if (!size) + { + switch (codec) + { + case VC_CONTAINER_CODEC_I420: + case VC_CONTAINER_CODEC_YV12: + size = width * height * 3 / 2; + break; + default: break; + } + } + + if (!width || !height || !size) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if (block_size) *block_size = size; + if (c) *c = codec; + if (w) *w = width; + if (h) *h = height; + if (fr_num) *fr_num = fn; + if (fr_den) *fr_den = fd; + if (block_size) *block_size = size; + + return VC_CONTAINER_SUCCESS; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_reader_read( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int size; + + if (module->status != VC_CONTAINER_SUCCESS) + return module->status; + + if (module->yuv4mpeg2 && !module->block_offset && + !module->frame_header) + { + module->status = read_yuv4mpeg2_frame_header(ctx); + if (module->status != VC_CONTAINER_SUCCESS) + return module->status; + + module->frame_header = true; + } + + if (!module->block_offset) + packet->pts = packet->dts = module->frames * INT64_C(1000000) * + ctx->tracks[0]->format->type->video.frame_rate_den / + ctx->tracks[0]->format->type->video.frame_rate_num; + else + packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN; + packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END | + VC_CONTAINER_PACKET_FLAG_KEYFRAME; + if (!module->block_offset) + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + packet->frame_size = module->block_size; + packet->size = module->block_size - module->block_offset; + packet->track = 0; + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + { + size = SKIP_BYTES(ctx, packet->size); + module->block_offset = 0; + module->frames++; + module->frame_header = 0; + module->status = STREAM_STATUS(ctx); + return module->status; + } + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + size = MIN(module->block_size - module->block_offset, packet->buffer_size); + size = READ_BYTES(ctx, packet->data, size); + module->block_offset += size; + packet->size = size; + + if (module->block_offset == module->block_size) + { + module->block_offset = 0; + module->frame_header = 0; + module->frames++; + } + + module->status = size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(ctx); + return module->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_reader_seek( VC_CONTAINER_T *ctx, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(mode); + + module->frames = *offset * + ctx->tracks[0]->format->type->video.frame_rate_num / + ctx->tracks[0]->format->type->video.frame_rate_den / INT64_C(1000000); + module->block_offset = 0; + + if ((flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && + module->frames * INT64_C(1000000) * + ctx->tracks[0]->format->type->video.frame_rate_den / + ctx->tracks[0]->format->type->video.frame_rate_num < *offset) + module->frames++; + + module->frame_header = 0; + + module->status = + SEEK(ctx, module->data_offset + module->frames * + (module->block_size + module->frame_header_size)); + + return module->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_reader_close( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + for (; ctx->tracks_num > 0; ctx->tracks_num--) + vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + const char *extension = vc_uri_path_extension(ctx->priv->uri); + VC_CONTAINER_MODULE_T *module = 0; + bool yuv4mpeg2 = false; + uint8_t h[10]; + + /* Check if the user has specified a container */ + vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); + + /* Check for the YUV4MPEG2 signature */ + if (PEEK_BYTES(ctx, h, sizeof(h)) != sizeof(h)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if (!memcmp(h, "YUV4MPEG2 ", sizeof(h))) + yuv4mpeg2 = true; + + /* Or check if the extension is supported */ + if (!yuv4mpeg2 && + !(extension && !strcasecmp(extension, "yuv"))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + LOG_DEBUG(ctx, "using raw video reader"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if (!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + ctx->priv->module = module; + ctx->tracks_num = 1; + ctx->tracks = &module->track; + ctx->tracks[0] = vc_container_allocate_track(ctx, 0); + if (!ctx->tracks[0]) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + ctx->tracks[0]->is_enabled = true; + ctx->tracks[0]->format->type->video.frame_rate_num = 25; + ctx->tracks[0]->format->type->video.frame_rate_den = 1; + ctx->tracks[0]->format->type->video.par_num = 1; + ctx->tracks[0]->format->type->video.par_den = 1; + + if (yuv4mpeg2) + { + status = read_yuv4mpeg2_file_header(ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + module->data_offset = STREAM_POSITION(ctx); + + status = read_yuv4mpeg2_frame_header(ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + module->frame_header = true; + } + else + { + VC_CONTAINER_FOURCC_T codec; + unsigned int width, height, fr_num, fr_den, block_size; + + status = rawvideo_parse_uri(ctx, &codec, &width, &height, + &fr_num, &fr_den, &block_size); + if (status != VC_CONTAINER_SUCCESS) + goto error; + ctx->tracks[0]->format->codec = codec; + ctx->tracks[0]->format->type->video.width = width; + ctx->tracks[0]->format->type->video.height = height; + if (fr_num && fr_den) + { + ctx->tracks[0]->format->type->video.frame_rate_num = fr_num; + ctx->tracks[0]->format->type->video.frame_rate_den = fr_den; + } + module->block_size = block_size; + } + + /* + * We now have all the information we really need to start playing the stream + */ + + LOG_INFO(ctx, "rawvideo %4.4s/%ix%i/fps:%i:%i/size:%i", + (char *)&ctx->tracks[0]->format->codec, + ctx->tracks[0]->format->type->video.width, + ctx->tracks[0]->format->type->video.height, + ctx->tracks[0]->format->type->video.frame_rate_num, + ctx->tracks[0]->format->type->video.frame_rate_den, module->block_size); + ctx->priv->pf_close = rawvideo_reader_close; + ctx->priv->pf_read = rawvideo_reader_read; + ctx->priv->pf_seek = rawvideo_reader_seek; + module->yuv4mpeg2 = yuv4mpeg2; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(ctx, "rawvideo: error opening stream (%i)", status); + rawvideo_reader_close(ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open rawvideo_reader_open +#endif diff --git a/containers/raw/raw_video_writer.c b/containers/raw/raw_video_writer.c new file mode 100755 index 0000000..49b7c7c --- /dev/null +++ b/containers/raw/raw_video_writer.c @@ -0,0 +1,264 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +#include "raw_video_common.h" + +/****************************************************************************** +Defines. +******************************************************************************/ + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + bool yuv4mpeg2; + bool header_done; + bool non_standard; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_write_header( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int size; + char line[128]; + const char *id; + + size = snprintf(line, sizeof(line), "YUV4MPEG2 W%i H%i", + ctx->tracks[0]->format->type->video.width, + ctx->tracks[0]->format->type->video.height); + if (size >= sizeof(line)) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + WRITE_BYTES(ctx, line, size); + + if (ctx->tracks[0]->format->type->video.frame_rate_num && + ctx->tracks[0]->format->type->video.frame_rate_den) + { + size = snprintf(line, sizeof(line), " F%i:%i", + ctx->tracks[0]->format->type->video.frame_rate_num, + ctx->tracks[0]->format->type->video.frame_rate_den); + if (size >= sizeof(line)) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + WRITE_BYTES(ctx, line, size); + } + + if (ctx->tracks[0]->format->type->video.par_num && + ctx->tracks[0]->format->type->video.par_den) + { + size = snprintf(line, sizeof(line), " A%i:%i", + ctx->tracks[0]->format->type->video.par_num, + ctx->tracks[0]->format->type->video.par_den); + if (size >= sizeof(line)) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + WRITE_BYTES(ctx, line, size); + } + + if (to_yuv4mpeg2(ctx->tracks[0]->format->codec, &id, 0, 0)) + { + size = snprintf(line, sizeof(line), " C%s", id); + } + else + { + module->non_standard = true; + size = snprintf(line, sizeof(line), " C%4.4s", + (char *)&ctx->tracks[0]->format->codec); + } + if (size >= sizeof(line)) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + WRITE_BYTES(ctx, line, size); + + _WRITE_U8(ctx, 0x0a); + module->header_done = true; + return STREAM_STATUS(ctx); +} + +static VC_CONTAINER_STATUS_T simple_write_add_track( VC_CONTAINER_T *ctx, + VC_CONTAINER_ES_FORMAT_T *format ) +{ + VC_CONTAINER_STATUS_T status; + + /* Sanity check that we support the type of track being created */ + if (ctx->tracks_num) + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + if (format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) + return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; + + /* Allocate and initialise track data */ + ctx->tracks[0] = vc_container_allocate_track(ctx, 0); + if (!ctx->tracks[0]) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + status = vc_container_track_allocate_extradata(ctx, + ctx->tracks[0], format->extradata_size); + if(status != VC_CONTAINER_SUCCESS) + return status; + + vc_container_format_copy(ctx->tracks[0]->format, format, + format->extradata_size); + ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_writer_close( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + for (; ctx->tracks_num > 0; ctx->tracks_num--) + vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_writer_write( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_STATUS_T status; + + if (module->yuv4mpeg2 && !module->header_done) + { + status = rawvideo_write_header(ctx); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + if (module->yuv4mpeg2 && + (packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)) + { + /* Write the metadata */ + WRITE_BYTES(ctx, "FRAME", sizeof("FRAME")-1); + + /* For formats not supported by the YUV4MPEG2 spec, we prepend + * each frame with its size */ + if (module->non_standard) + { + unsigned int size; + char line[32]; + size = snprintf(line, sizeof(line), " S%i", + packet->frame_size ? packet->frame_size : packet->size); + if (size < sizeof(line)) + WRITE_BYTES(ctx, line, size); + } + + _WRITE_U8(ctx, 0x0a); + } + + /* Write the elementary stream */ + WRITE_BYTES(ctx, packet->data, packet->size); + + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rawvideo_writer_control( VC_CONTAINER_T *ctx, + VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_ES_FORMAT_T *format; + + switch (operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + format = (VC_CONTAINER_ES_FORMAT_T *)va_arg(args, VC_CONTAINER_ES_FORMAT_T *); + return simple_write_add_track(ctx, format); + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + return module->yuv4mpeg2 ? + rawvideo_write_header( ctx ) : VC_CONTAINER_SUCCESS; + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + const char *extension = vc_uri_path_extension(ctx->priv->uri); + VC_CONTAINER_MODULE_T *module; + bool yuv4mpeg2 = false; + + /* Check if the user has specified a container */ + vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(!strcasecmp(extension, "y4m") || !strcasecmp(extension, "yuv4mpeg2")) + yuv4mpeg2 = true; + if(!yuv4mpeg2 && strcasecmp(extension, "yuv")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + LOG_DEBUG(ctx, "using rawvideo writer"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + ctx->priv->module = module; + ctx->tracks = &module->track; + module->yuv4mpeg2 = yuv4mpeg2; + + ctx->priv->pf_close = rawvideo_writer_close; + ctx->priv->pf_write = rawvideo_writer_write; + ctx->priv->pf_control = rawvideo_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(ctx, "rawvideo: error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open rawvideo_writer_open +#endif diff --git a/containers/rcv/CMakeLists.txt b/containers/rcv/CMakeLists.txt new file mode 100755 index 0000000..9407f58 --- /dev/null +++ b/containers/rcv/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_rcv ${LIBRARY_TYPE} rcv_reader.c) + +target_link_libraries(reader_rcv containers) + +install(TARGETS reader_rcv DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/rcv/rcv_reader.c b/containers/rcv/rcv_reader.c new file mode 100755 index 0000000..4d4db27 --- /dev/null +++ b/containers/rcv/rcv_reader.c @@ -0,0 +1,358 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_index.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ + +#define LI32(b) (((b)[3]<<24)|((b)[2]<<16)|((b)[1]<<8)|((b)[0])) +#define LI24(b) (((b)[2]<<16)|((b)[1]<<8)|((b)[0])) + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct { + unsigned int num_frames : 24; + unsigned int constant_c5 : 8; + int constant_4; + uint32_t struct_c; + uint32_t vert_size; + uint32_t horiz_size; + int constant_c; + uint32_t struct_b[2]; + uint32_t framerate; +} RCV_FILE_HEADER_T; + +typedef struct { + unsigned int framesize : 24; + unsigned int res : 7; + unsigned int keyframe : 1; + uint32_t timestamp; +} RCV_FRAME_HEADER_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + uint8_t extradata[4]; + uint8_t mid_frame; + uint32_t frame_read; + RCV_FRAME_HEADER_T frame; + VC_CONTAINER_INDEX_T *index; /* index of key frames */ + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +static VC_CONTAINER_STATUS_T rcv_read_header(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + RCV_FILE_HEADER_T header; + uint8_t dummy[36]; + + if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS; + + header.num_frames = LI24(dummy); + header.constant_c5 = dummy[3]; + header.constant_4 = LI32(dummy+4); + + // extradata is just struct_c from the header + memcpy(module->extradata, dummy+8, 4); + module->track->format->extradata = module->extradata; + module->track->format->extradata_size = 4; + + module->track->format->type->video.height = LI32(dummy+12); + module->track->format->type->video.width = LI32(dummy+16); + + header.constant_c = LI32(dummy+20); + memcpy(header.struct_b, dummy+24, 8); + header.framerate = LI32(dummy+32); + + if(header.constant_c5 != 0xc5 || header.constant_4 != 0x4 || header.constant_c != 0xc) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if(header.framerate != 0 && header.framerate != 0xffffffffUL) + { + module->track->format->type->video.frame_rate_num = header.framerate; + module->track->format->type->video.frame_rate_den = 1; + } + + // fill in general information + if(header.num_frames != (1<<24)-1 && header.framerate != 0 && header.framerate != 0xffffffffUL) + p_ctx->duration = ((int64_t) header.num_frames * 1000000LL) / (int64_t) header.framerate; + + // we're happy that this is an rcv file + SKIP_BYTES(p_ctx, sizeof(dummy)); + + return STREAM_STATUS(p_ctx); +} + +/***************************************************************************** + * Utility function to seek to the keyframe nearest the given timestamp. + * + * @param p_ctx Pointer to the container context. + * @param timestamp The requested time. On success, this is updated with the time of the selected keyframe. + * @param later If true, the selected frame is the earliest keyframe with a time greater or equal to timestamp. + * If false, the selected frame is the latest keyframe with a time earlier or equal to timestamp. + * @return Status code. + */ +static VC_CONTAINER_STATUS_T rcv_seek_nearest_keyframe(VC_CONTAINER_T *p_ctx, int64_t *timestamp, int later) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + int64_t prev_keyframe_offset = sizeof(RCV_FILE_HEADER_T); /* set to very first frame */ + int64_t prev_keyframe_timestamp = 0; + int use_prev_keyframe = !later; + + if(use_prev_keyframe || (module->frame.timestamp * 1000LL > *timestamp)) + { + /* A seek has been requested to an earlier keyframe, so rewind to the beginning + * of the stream since there's no information available on previous frames */ + SEEK(p_ctx, sizeof(RCV_FILE_HEADER_T)); + memset(&module->frame, 0, sizeof(RCV_FRAME_HEADER_T)); + module->mid_frame = 0; + module->frame_read = 0; + } + + if(module->mid_frame) + { + /* Seek back to the start of the current frame */ + SEEK(p_ctx, STREAM_POSITION(p_ctx) - module->frame_read - sizeof(RCV_FILE_HEADER_T)); + module->mid_frame = 0; + module->frame_read = 0; + } + + while(1) + { + if(PEEK_BYTES(p_ctx, &module->frame, sizeof(RCV_FRAME_HEADER_T)) != sizeof(RCV_FRAME_HEADER_T)) + { + status = VC_CONTAINER_ERROR_EOS; + break; + } + + if(module->frame.keyframe) + { + if(module->index) + vc_container_index_add(module->index, module->frame.timestamp * 1000LL, STREAM_POSITION(p_ctx)); + + if((module->frame.timestamp * 1000LL) >= *timestamp) + { + if((module->frame.timestamp * 1000LL) == *timestamp) + use_prev_keyframe = 0; + + *timestamp = module->frame.timestamp * 1000LL; + + break; + } + + prev_keyframe_offset = STREAM_POSITION(p_ctx); + prev_keyframe_timestamp = module->frame.timestamp * 1000LL; + } + + SKIP_BYTES(p_ctx, module->frame.framesize + sizeof(RCV_FRAME_HEADER_T)); + } + + if(use_prev_keyframe) + { + *timestamp = prev_keyframe_timestamp; + status = SEEK(p_ctx, prev_keyframe_offset); + } + + return status; +} + +/***************************************************************************** +Functions exported as part of the Container Module API +*****************************************************************************/ +static VC_CONTAINER_STATUS_T rcv_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int size; + + if(!module->mid_frame) + { + /* Save the current position for updating the indexer */ + int64_t position = STREAM_POSITION(p_ctx); + + if(READ_BYTES(p_ctx, &module->frame, sizeof(RCV_FRAME_HEADER_T)) != sizeof(RCV_FRAME_HEADER_T)) + return VC_CONTAINER_ERROR_EOS; + module->mid_frame = 1; + module->frame_read = 0; + + if(module->index && module->frame.keyframe) + vc_container_index_add(module->index, (int64_t)module->frame.timestamp * 1000LL, position); + } + + packet->size = module->frame.framesize; + packet->dts = packet->pts = module->frame.timestamp * 1000LL; + packet->track = 0; + packet->flags = 0; + if(module->frame_read == 0) + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + if(module->frame.keyframe) + packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + size = SKIP_BYTES(p_ctx, module->frame.framesize - module->frame_read); + if((module->frame_read += size) == module->frame.framesize) + { + module->frame_read = 0; + module->mid_frame = 0; + } + return STREAM_STATUS(p_ctx); + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + size = MIN(module->frame.framesize - module->frame_read, packet->buffer_size); + size = READ_BYTES(p_ctx, packet->data, size); + if((module->frame_read += size) == module->frame.framesize) + { + module->frame_read = 0; + module->mid_frame = 0; + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + } + packet->size = size; + + return size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rcv_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + int past = 1; + int64_t position; + int64_t timestamp = *offset; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FAILED; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(mode); + + if(module->index) + status = vc_container_index_get(module->index, flags & VC_CONTAINER_SEEK_FLAG_FORWARD, ×tamp, &position, &past); + + if(status == VC_CONTAINER_SUCCESS && !past) + { + /* Indexed keyframe found */ + module->frame_read = 0; + module->mid_frame = 0; + *offset = timestamp; + status = SEEK(p_ctx, position); + } + else + { + /* No indexed keyframe found, so seek through all frames */ + status = rcv_seek_nearest_keyframe(p_ctx, offset, flags & VC_CONTAINER_SEEK_FLAG_FORWARD); + } + + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rcv_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + + if(module->index) + vc_container_index_free(module->index); + + free(module); + + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + uint8_t dummy[8]; + + /* Quick check for a valid file header */ + if((PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) || + dummy[3] != 0xc5 || LI32(dummy+4) != 0x4) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks_num = 1; + p_ctx->tracks = &module->track; + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_WMV3; + p_ctx->tracks[0]->is_enabled = true; + + if((status = rcv_read_header(p_ctx)) != VC_CONTAINER_SUCCESS) goto error; + + LOG_DEBUG(p_ctx, "using rcv reader"); + + if(vc_container_index_create(&module->index, 512) == VC_CONTAINER_SUCCESS) + vc_container_index_add(module->index, 0LL, STREAM_POSITION(p_ctx)); + + if(STREAM_SEEKABLE(p_ctx)) + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + p_ctx->priv->pf_close = rcv_reader_close; + p_ctx->priv->pf_read = rcv_reader_read; + p_ctx->priv->pf_seek = rcv_reader_seek; + return VC_CONTAINER_SUCCESS; + + error: + if(module) rcv_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function +********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open rcv_reader_open +#endif diff --git a/containers/rtp/CMakeLists.txt b/containers/rtp/CMakeLists.txt new file mode 100755 index 0000000..e6ba3a9 --- /dev/null +++ b/containers/rtp/CMakeLists.txt @@ -0,0 +1,17 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +set(rtp_SRCS ${rtp_SRCS} rtp_reader.c) +set(rtp_SRCS ${rtp_SRCS} rtp_h264.c) +set(rtp_SRCS ${rtp_SRCS} rtp_mpeg4.c) +set(rtp_SRCS ${rtp_SRCS} rtp_base64.c) +add_library(reader_rtp ${LIBRARY_TYPE} ${rtp_SRCS}) + +target_link_libraries(reader_rtp containers) + +install(TARGETS reader_rtp DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/rtp/rtp_base64.c b/containers/rtp/rtp_base64.c new file mode 100755 index 0000000..f0e873f --- /dev/null +++ b/containers/rtp/rtp_base64.c @@ -0,0 +1,165 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "rtp_base64.h" + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +#define LOWEST_BASE64_CHAR '+' +#define HIGHEST_BASE64_CHAR 'z' +#define IN_BASE64_RANGE(C) ((C) >= LOWEST_BASE64_CHAR && (C) <= HIGHEST_BASE64_CHAR) + +/** Used as a marker in the lookup table to indicate an invalid Base64 character */ +#define INVALID 0xFF + +/* Reduced lookup table for translating a character to a 6-bit value. The + * table starts at the lowest Base64 character, '+' */ +uint8_t base64_decode_lookup[] = { + 62, INVALID, 62, INVALID, 63, /* '+' to '/' */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* '0' to '9' */ + INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, /* ':' to '@' */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'A' to 'T' */ + 20, 21, 22, 23, 24, 25, /* 'U' to 'Z' */ + INVALID, INVALID, INVALID, INVALID, 63, INVALID, /* '[' to '`' */ + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, /* 'a' to 'r' */ + 44, 45, 46, 47, 48, 49, 50, 51 /* 's' to 'z' */ +}; + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/****************************************************************************** +Function prototypes +******************************************************************************/ + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/***************************************************************************** +Functions exported as part of the Base64 API + *****************************************************************************/ + +/*****************************************************************************/ +uint32_t rtp_base64_byte_length(const char *str, uint32_t str_len) +{ + uint32_t character_count = 0; + uint32_t ii; + char cc; + + /* Scan through string until either a pad ('=') character or the end is + * reached. Ignore characters that are not part of the Base64 alphabet. + * Number of bytes should then be 3/4 of the character count. */ + + for (ii = 0; ii < str_len; ii++) + { + cc = *str++; + if (cc == '=') + break; /* Found a pad character: stop */ + + if (!IN_BASE64_RANGE(cc)) + continue; /* Ignore invalid character */ + + if (base64_decode_lookup[cc - LOWEST_BASE64_CHAR] != INVALID) + character_count++; + } + + return (character_count * 3) >> 2; +} + +/*****************************************************************************/ +uint8_t *rtp_base64_decode(const char *str, uint32_t str_len, uint8_t *buffer, uint32_t buffer_len) +{ + uint32_t character_count = 0; + uint32_t value = 0; + uint32_t ii; + char cc; + uint8_t lookup; + + /* Build up sets of four characters (ignoring invalid ones) to generate + * triplets of bytes, until either the end of the string or the pad ('=') + * characters are reached. */ + + for (ii = 0; ii < str_len; ii++) + { + cc = *str++; + if (cc == '=') + break; /* Found a pad character: stop */ + + if (!IN_BASE64_RANGE(cc)) + continue; /* Ignore invalid character */ + + lookup = base64_decode_lookup[cc - LOWEST_BASE64_CHAR]; + if (lookup == INVALID) + continue; /* Ignore invalid character */ + + value = (value << 6) | lookup; + character_count++; + + if (character_count == 4) + { + if (buffer_len < 3) + return NULL; /* Not enough room in the output buffer */ + + *buffer++ = (uint8_t)(value >> 16); + *buffer++ = (uint8_t)(value >> 8); + *buffer++ = (uint8_t)(value ); + buffer_len -= 3; + + character_count = 0; + value = 0; + } + } + + /* If there were extra characters on the end, these need to be handled to get + * the last one or two bytes. */ + + switch (character_count) + { + case 0: /* Nothing more to do, the final bytes were converted in the loop */ + break; + case 2: /* One additional byte, padded with four zero bits */ + if (!buffer_len) + return NULL; + *buffer++ = (uint8_t)(value >> 4); + break; + case 3: /* Two additional bytes, padded with two zero bits */ + if (buffer_len < 2) + return NULL; + *buffer++ = (uint8_t)(value >> 10); + *buffer++ = (uint8_t)(value >> 2); + break; + default: /* This is an invalid Base64 encoding */ + return NULL; + } + + /* Return number of bytes written to the buffer */ + return buffer; +} diff --git a/containers/rtp/rtp_base64.h b/containers/rtp/rtp_base64.h new file mode 100755 index 0000000..2cb6a76 --- /dev/null +++ b/containers/rtp/rtp_base64.h @@ -0,0 +1,49 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _RTP_BASE64_H_ +#define _RTP_BASE64_H_ + +#include "containers/containers.h" + +/** Returns the number of bytes encoded by the given Base64 encoded string. + * + * \param str The Base64 encoded string. + * \param str_len The number of characters in the string. + * \return The number of bytes that can be decoded. */ +uint32_t rtp_base64_byte_length(const char *str, uint32_t str_len); + +/** Decodes a Base64 encoded string into a byte buffer. + * + * \param str The Base64 encoded string. + * \param str_len The number of characters in the string. + * \param buffer The buffer to receive the decoded output. + * \param buffer_len The maximum number of bytes to put in the buffer. + * \return Pointer to byte after the last one converted, or NULL on error. */ +uint8_t *rtp_base64_decode(const char *str, uint32_t str_len, uint8_t *buffer, uint32_t buffer_len); + +#endif /* _RTP_BASE64_H_ */ diff --git a/containers/rtp/rtp_h264.c b/containers/rtp/rtp_h264.c new file mode 100755 index 0000000..e7f0f83 --- /dev/null +++ b/containers/rtp/rtp_h264.c @@ -0,0 +1,803 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#include "containers/containers.h" + +#include "containers/core/containers_logging.h" +#include "containers/core/containers_list.h" +#include "containers/core/containers_bits.h" +#include "rtp_priv.h" +#include "rtp_base64.h" +#include "rtp_h264.h" + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +/** H.264 payload flag bits */ +typedef enum +{ + H264F_NEXT_PACKET_IS_START = 0, + H264F_INSIDE_FRAGMENT, + H264F_OUTPUT_NAL_HEADER, +} h264_flag_bit_t; + +/** Bit mask to extract F zero bit from NAL unit header */ +#define NAL_UNIT_FZERO_MASK 0x80 +/** Bit mask to extract NAL unit type from NAL unit header */ +#define NAL_UNIT_TYPE_MASK 0x1F + +/** NAL unit type codes */ +enum +{ + /* 0 unspecified */ + NAL_UNIT_NON_IDR = 1, + NAL_UNIT_PARTITION_A = 2, + NAL_UNIT_PARTITION_B = 3, + NAL_UNIT_PARTITION_C = 4, + NAL_UNIT_IDR = 5, + NAL_UNIT_SEI = 6, + NAL_UNIT_SEQUENCE_PARAMETER_SET = 7, + NAL_UNIT_PICTURE_PARAMETER_SET = 8, + NAL_UNIT_ACCESS_UNIT_DELIMITER = 9, + NAL_UNIT_END_OF_SEQUENCE = 10, + NAL_UNIT_END_OF_STREAM = 11, + NAL_UNIT_FILLER = 12, + NAL_UNIT_EXT_SEQUENCE_PARAMETER_SET = 13, + NAL_UNIT_PREFIX = 14, + NAL_UNIT_SUBSET_SEQUENCE_PARAMETER_SET = 15, + /* 16 to 18 reserved */ + NAL_UNIT_AUXILIARY = 19, + NAL_UNIT_EXTENSION = 20, + /* 21 to 23 reserved */ + NAL_UNIT_STAP_A = 24, + NAL_UNIT_STAP_B = 25, + NAL_UNIT_MTAP16 = 26, + NAL_UNIT_MTAP24 = 27, + NAL_UNIT_FU_A = 28, + NAL_UNIT_FU_B = 29, + /* 30 to 31 unspecified */ +}; + +/** Fragment unit header indicator bits */ +typedef enum +{ + FRAGMENT_UNIT_HEADER_RESERVED = 5, + FRAGMENT_UNIT_HEADER_END = 6, + FRAGMENT_UNIT_HEADER_START = 7, +} fragment_unit_header_bit_t; + +#define MACROBLOCK_WIDTH 16 +#define MACROBLOCK_HEIGHT 16 + +/** H.264 RTP timestamp clock rate */ +#define H264_TIMESTAMP_CLOCK 90000 + +typedef enum +{ + CHROMA_FORMAT_MONO = 0, + CHROMA_FORMAT_YUV_420 = 1, + CHROMA_FORMAT_YUV_422 = 2, + CHROMA_FORMAT_YUV_444 = 3, + CHROMA_FORMAT_YUV_444_PLANAR = 4, + CHROMA_FORMAT_RGB = 5, +} CHROMA_FORMAT_T; + +uint32_t chroma_sub_width[] = { + 1, 2, 2, 1, 1, 1 +}; + +uint32_t chroma_sub_height[] = { + 1, 2, 1, 1, 1, 1 +}; + +/****************************************************************************** +Type definitions +******************************************************************************/ + +typedef struct h264_payload_tag +{ + uint32_t nal_unit_size; /**< Number of NAL unit bytes left to write */ + uint8_t flags; /**< H.264 payload flags */ + uint8_t header_bytes_to_write; /**< Number of start code bytes left to write */ + uint8_t nal_header; /**< Header for next NAL unit */ +} H264_PAYLOAD_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/**************************************************************************//** + * Remove emulation prevention bytes from a buffer. + * These are 0x03 bytes inserted to prevent misinterprentation of a byte + * sequence in a buffer as a start code. + * + * @param sprop The buffer from which bytes are to be removed. + * @param sprop_size The number of bytes in the buffer. + * @return The new number of bytes in the buffer. + */ +static uint32_t h264_remove_emulation_prevention_bytes(uint8_t *sprop, + uint32_t sprop_size) +{ + uint32_t offset = 0; + uint8_t nal_unit_type = sprop[offset++]; + uint32_t new_sprop_size = sprop_size; + uint8_t first_byte, second_byte; + + nal_unit_type &= 0x1F; /* Just keep NAL unit type bits */ + + /* Certain NAL unit types need a byte triplet passed first */ + if (nal_unit_type == NAL_UNIT_PREFIX || nal_unit_type == NAL_UNIT_EXTENSION) + offset += 3; + + /* Make sure there is enough data for there to be a 0x00 0x00 0x03 sequence */ + if (offset + 2 >= new_sprop_size) + return new_sprop_size; + + /* Keep a rolling set of the last couple of bytes */ + first_byte = sprop[offset++]; + second_byte = sprop[offset++]; + + while (offset < new_sprop_size) + { + uint8_t next_byte = sprop[offset]; + + if (!first_byte && !second_byte && next_byte == 0x03) + { + /* Remove the emulation prevention byte (0x03) */ + new_sprop_size--; + if (offset == new_sprop_size) /* No more data to check */ + break; + memmove(&sprop[offset], &sprop[offset + 1], new_sprop_size - offset); + next_byte = sprop[offset]; + } else + offset++; + + first_byte = second_byte; + second_byte = next_byte; + } + + return new_sprop_size; +} + +/**************************************************************************//** + * Skip a scaling list in a bit stream. + * + * @param p_ctx The container context. + * @param sprop The bit stream containing the scaling list. + * @param size_of_scaling_list The size of the scaling list. + */ +static void h264_skip_scaling_list(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_BITS_T *sprop, + uint32_t size_of_scaling_list) +{ + uint32_t last_scale = 8; + uint32_t next_scale = 8; + int32_t delta_scale; + uint32_t jj; + + /* Algorithm taken from H.264 section 7.3.2.1.1.1 */ + for (jj = 0; jj < size_of_scaling_list; jj++) + { + if (next_scale) + { + delta_scale = BITS_READ_S32_EXP(p_ctx, sprop, "delta_scale"); + next_scale = (last_scale + delta_scale + 256) & 0xFF; + + if (next_scale) + last_scale = next_scale; + } + } +} + +/**************************************************************************//** + * Get the chroma format from the bit stream. + * + * @param p_ctx The container context. + * @param sprop The bit stream containing the scaling list. + * @return The chroma format index. + */ +static uint32_t h264_get_chroma_format(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_BITS_T *sprop) +{ + uint32_t chroma_format_idc; + + chroma_format_idc = BITS_READ_U32_EXP(p_ctx, sprop, "chroma_format_idc"); + if (chroma_format_idc == 3 && BITS_READ_U32(p_ctx, sprop, 1, "separate_colour_plane_flag")) + chroma_format_idc = CHROMA_FORMAT_YUV_444_PLANAR; + + BITS_SKIP_EXP(p_ctx, sprop, "bit_depth_luma_minus8"); + BITS_SKIP_EXP(p_ctx, sprop, "bit_depth_chroma_minus8"); + BITS_SKIP(p_ctx, sprop, 1, "qpprime_y_zero_transform_bypass_flag"); + + if (BITS_READ_U32(p_ctx, sprop, 1, "seq_scaling_matrix_present_flag")) + { + uint32_t scaling_lists = (chroma_format_idc == 3) ? 12 : 8; + uint32_t ii; + + for (ii = 0; ii < scaling_lists; ii++) + { + if (BITS_READ_U32(p_ctx, sprop, 1, "seq_scaling_list_present_flag")) + h264_skip_scaling_list(p_ctx, sprop, (ii < 6) ? 16 : 64); + } + } + + return chroma_format_idc; +} + +/**************************************************************************//** + * Decode an H.264 sequence parameter set and update track information. + * + * @param p_ctx The RTP container context. + * @param track The track to be updated. + * @param sprop The bit stream containing the sequence parameter set. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_decode_sequence_parameter_set(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_BITS_T *sprop) +{ + VC_CONTAINER_VIDEO_FORMAT_T *video = &track->format->type->video; + uint32_t pic_order_cnt_type, chroma_format_idc; + uint32_t pic_width_in_mbs_minus1, pic_height_in_map_units_minus1, frame_mbs_only_flag; + uint32_t frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset, frame_crop_bottom_offset; + uint8_t profile_idc; + + /* This structure is defined by H.264 section 7.3.2.1.1 */ + profile_idc = BITS_READ_U8(p_ctx, sprop, 8, "profile_idc"); + BITS_SKIP(p_ctx, sprop, 16, "Rest of profile_level_id"); + + BITS_READ_U32_EXP(p_ctx, sprop, "seq_parameter_set_id"); + + chroma_format_idc = CHROMA_FORMAT_RGB; + if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || + profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || + profile_idc == 86 || profile_idc == 118 || profile_idc == 128) + { + chroma_format_idc = h264_get_chroma_format(p_ctx, sprop); + if (chroma_format_idc > CHROMA_FORMAT_YUV_444_PLANAR) + goto error; + } + + BITS_SKIP_EXP(p_ctx, sprop, "log2_max_frame_num_minus4"); + pic_order_cnt_type = BITS_READ_U32_EXP(p_ctx, sprop, "pic_order_cnt_type"); + if (pic_order_cnt_type == 0) + { + BITS_SKIP_EXP(p_ctx, sprop, "log2_max_pic_order_cnt_lsb_minus4"); + } + else if (pic_order_cnt_type == 1) + { + uint32_t num_ref_frames_in_pic_order_cnt_cycle; + uint32_t ii; + + BITS_SKIP(p_ctx, sprop, 1, "delta_pic_order_always_zero_flag"); + BITS_SKIP_EXP(p_ctx, sprop, "offset_for_non_ref_pic"); + BITS_SKIP_EXP(p_ctx, sprop, "offset_for_top_to_bottom_field"); + num_ref_frames_in_pic_order_cnt_cycle = BITS_READ_U32_EXP(p_ctx, sprop, "num_ref_frames_in_pic_order_cnt_cycle"); + + for (ii = 0; ii < num_ref_frames_in_pic_order_cnt_cycle; ii++) + BITS_SKIP_EXP(p_ctx, sprop, "offset_for_ref_frame"); + } + + BITS_SKIP_EXP(p_ctx, sprop, "max_num_ref_frames"); + BITS_SKIP(p_ctx, sprop, 1, "gaps_in_frame_num_value_allowed_flag"); + + pic_width_in_mbs_minus1 = BITS_READ_U32_EXP(p_ctx, sprop, "pic_width_in_mbs_minus1"); + pic_height_in_map_units_minus1 = BITS_READ_U32_EXP(p_ctx, sprop, "pic_height_in_map_units_minus1"); + frame_mbs_only_flag = BITS_READ_U32(p_ctx, sprop, 1, "frame_mbs_only_flag"); + + /* Can now set the overall width and height in pixels */ + video->width = (pic_width_in_mbs_minus1 + 1) * MACROBLOCK_WIDTH; + video->height = (2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * MACROBLOCK_HEIGHT; + + if (!frame_mbs_only_flag) + BITS_SKIP(p_ctx, sprop, 1, "mb_adaptive_frame_field_flag"); + BITS_SKIP(p_ctx, sprop, 1, "direct_8x8_inference_flag"); + + if (BITS_READ_U32(p_ctx, sprop, 1, "frame_cropping_flag")) + { + /* Visible area is restricted */ + frame_crop_left_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_left_offset"); + frame_crop_right_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_right_offset"); + frame_crop_top_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_top_offset"); + frame_crop_bottom_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_bottom_offset"); + + /* Need to adjust offsets for 4:2:0 and 4:2:2 chroma formats and field/frame flag */ + frame_crop_left_offset *= chroma_sub_width[chroma_format_idc]; + frame_crop_right_offset *= chroma_sub_width[chroma_format_idc]; + frame_crop_top_offset *= chroma_sub_height[chroma_format_idc] * (2 - frame_mbs_only_flag); + frame_crop_bottom_offset *= chroma_sub_height[chroma_format_idc] * (2 - frame_mbs_only_flag); + + if ((frame_crop_left_offset + frame_crop_right_offset) >= video->width || + (frame_crop_top_offset + frame_crop_bottom_offset) >= video->height) + { + LOG_ERROR(p_ctx, "H.264: frame crop offsets (%u, %u, %u, %u) larger than frame (%u, %u)", + frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset, + frame_crop_bottom_offset, video->width, video->height); + goto error; + } + + video->x_offset = frame_crop_left_offset; + video->y_offset = frame_crop_top_offset; + video->visible_width = video->width - frame_crop_left_offset - frame_crop_right_offset; + video->visible_height = video->height - frame_crop_top_offset - frame_crop_bottom_offset; + } else { + video->visible_width = video->width; + video->visible_height = video->height; + } + + /* vui_parameters may follow, but these will not be decoded */ + + if (!BITS_VALID(p_ctx, sprop)) + goto error; + + return VC_CONTAINER_SUCCESS; + +error: + LOG_ERROR(p_ctx, "H.264: sequence_parameter_set failed to decode"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; +} + +/**************************************************************************//** + * Decode an H.264 sprop and update track information. + * + * @param p_ctx The RTP container context. + * @param track The track to be updated. + * @param sprop The bit stream containing the sprop. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_decode_sprop(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_BITS_T *sprop) +{ + switch (BITS_READ_U32(p_ctx, sprop, 8, "nal_unit_header") & NAL_UNIT_TYPE_MASK) + { + case NAL_UNIT_SEQUENCE_PARAMETER_SET: + return h264_decode_sequence_parameter_set(p_ctx, track, sprop); + case NAL_UNIT_PICTURE_PARAMETER_SET: + /* Not handled, but valid */ + return VC_CONTAINER_SUCCESS; + default: + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } +} + +/**************************************************************************//** + * Decode the sprop parameter sets URI parameter and update track information. + * + * @param p_ctx The RTP container context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_get_sprop_parameter_sets(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + VC_CONTAINER_STATUS_T status; + PARAMETER_T param; + size_t str_len; + uint32_t extradata_size = 0; + uint8_t *sprop; + const char *set; + const char *comma; + + /* Get the value of sprop-parameter-sets, base64 decode the (comma separated) + * sets, store all of them in track->priv->extradata and also decode to + * validate and fill in video format info. */ + + param.name = "sprop-parameter-sets"; + if (!vc_containers_list_find_entry(params, ¶m) || !param.value) + { + LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets is required, but not found"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* First pass, calculate total size of buffer needed */ + set = param.value; + do { + comma = strchr(set, ','); + str_len = comma ? (size_t)(comma - set) : strlen(set); + /* Allow space for the NAL unit and a start code */ + extradata_size += rtp_base64_byte_length(set, str_len) + 4; + set = comma + 1; + } while (comma); + + if (!extradata_size) + { + LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets doesn't contain useful data"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + status = vc_container_track_allocate_extradata(p_ctx, track, extradata_size); + if(status != VC_CONTAINER_SUCCESS) return status; + + track->format->extradata_size = extradata_size; + sprop = track->priv->extradata; + + /* Now decode the data into the buffer, and validate / use it to fill in format */ + set = param.value; + do { + uint8_t *next_sprop; + uint32_t sprop_size; + VC_CONTAINER_BITS_T sprop_stream; + + comma = strchr(set, ','); + str_len = comma ? (size_t)(comma - set) : strlen(set); + + /* Insert a start code (0x00000001 in network order) */ + *sprop++ = 0x00; *sprop++ = 0x00; *sprop++ = 0x00; *sprop++ = 0x01; + extradata_size -= 4; + + next_sprop = rtp_base64_decode(set, str_len, sprop, extradata_size); + if (!next_sprop) + { + LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets failed to decode"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + sprop_size = next_sprop - sprop; + if (sprop_size) + { + uint32_t new_sprop_size; + + /* Need to remove emulation prevention bytes before decoding */ + new_sprop_size = h264_remove_emulation_prevention_bytes(sprop, sprop_size); + + BITS_INIT(p_ctx, &sprop_stream, sprop, new_sprop_size); + status = h264_decode_sprop(p_ctx, track, &sprop_stream); + if(status != VC_CONTAINER_SUCCESS) return status; + + /* If necessary, decode sprop again, to put back the emulation prevention bytes */ + if (new_sprop_size != sprop_size) + rtp_base64_decode(set, str_len, sprop, sprop_size); + + extradata_size -= sprop_size; + sprop = next_sprop; + } + + set = comma + 1; + } while (comma); + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Check URI parameter list for unsupported features. + * + * @param p_ctx The RTP container context. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_check_unsupported_features(VC_CONTAINER_T *p_ctx, + const VC_CONTAINERS_LIST_T *params) +{ + uint32_t u32_unused; + + /* Limitation: interleaving not yet supported */ + if (rtp_get_parameter_u32(params, "sprop-interleaving-depth", &u32_unused) || + rtp_get_parameter_u32(params, "sprop-deint-buf-req", &u32_unused) || + rtp_get_parameter_u32(params, "sprop-init-buf-time", &u32_unused) || + rtp_get_parameter_u32(params, "sprop-max-don-diff", &u32_unused)) + { + LOG_ERROR(p_ctx, "H.264: Interleaved packetization is not supported"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Get and check the packetization mode URI parameter. + * + * @param p_ctx The RTP container context. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_get_packetization_mode(VC_CONTAINER_T *p_ctx, + const VC_CONTAINERS_LIST_T *params) +{ + uint32_t packetization_mode; + + if (rtp_get_parameter_u32(params, "packetization-mode", &packetization_mode)) + { + /* Only modes 0 and 1 are supported, no interleaving */ + if (packetization_mode > 1) + { + LOG_ERROR(p_ctx, "H.264: Unsupported packetization mode: %u", packetization_mode); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Initialise payload bit stream for a new RTP packet. + * + * @param p_ctx The RTP container context. + * @param t_module The track module with the new RTP packet. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_new_rtp_packet(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module) +{ + VC_CONTAINER_BITS_T *payload = &t_module->payload; + H264_PAYLOAD_T *extra = (H264_PAYLOAD_T *)t_module->extra; + uint8_t unit_header; + uint8_t fragment_header; + + /* Read the NAL unit type and process as necessary */ + unit_header = BITS_READ_U8(p_ctx, payload, 8, "nal_unit_header"); + + /* When the top bit is set, the NAL unit is invalid */ + if (unit_header & NAL_UNIT_FZERO_MASK) + { + LOG_DEBUG(p_ctx, "H.264: Invalid NAL unit (top bit of header set)"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* In most cases, a new packet means a new NAL unit, which will need a start code and the header */ + extra->header_bytes_to_write = 5; + extra->nal_header = unit_header; + extra->nal_unit_size = BITS_BYTES_AVAILABLE(p_ctx, payload); + + switch (unit_header & NAL_UNIT_TYPE_MASK) + { + case NAL_UNIT_STAP_A: + /* Single Time Aggregation Packet A */ + CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT); + /* Trigger reading NAL unit length and header */ + extra->nal_unit_size = 0; + break; + + case NAL_UNIT_FU_A: + /* Fragementation Unit A */ + fragment_header = BITS_READ_U8(p_ctx, payload, 8, "fragment_header"); + extra->nal_unit_size--; + + if (BIT_IS_CLEAR(fragment_header, FRAGMENT_UNIT_HEADER_START) || + BIT_IS_SET(extra->flags, H264F_INSIDE_FRAGMENT)) + { + /* This is a continuation packet, prevent start code and header from being output */ + extra->header_bytes_to_write = 0; + + /* If this is the end of a fragment, the next FU will be a new one */ + if (BIT_IS_SET(fragment_header, FRAGMENT_UNIT_HEADER_END)) + CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT); + } else { + /* Start of a new fragment. */ + SET_BIT(extra->flags, H264F_INSIDE_FRAGMENT); + + /* Merge type from fragment header and the rest from NAL unit header to form real NAL unit header */ + fragment_header &= NAL_UNIT_TYPE_MASK; + fragment_header |= (unit_header & ~NAL_UNIT_TYPE_MASK); + extra->nal_header = fragment_header; + } + break; + + case NAL_UNIT_STAP_B: + case NAL_UNIT_MTAP16: + case NAL_UNIT_MTAP24: + case NAL_UNIT_FU_B: + LOG_ERROR(p_ctx, "H.264: Unsupported RTP NAL unit type: %u", unit_header & NAL_UNIT_TYPE_MASK); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + default: + /* Single NAL unit case */ + CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT); + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * H.264 payload handler. + * Extracts/skips data from the payload according to the NAL unit headers. + * + * @param p_ctx The RTP container context. + * @param track The track being read. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T h264_payload_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags) +{ + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + VC_CONTAINER_BITS_T *payload = &t_module->payload; + H264_PAYLOAD_T *extra = (H264_PAYLOAD_T *)t_module->extra; + uint32_t packet_flags = 0; + uint8_t header_bytes_to_write; + uint32_t size, offset; + uint8_t *data_ptr; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + bool last_nal_unit_in_packet = false; + + if (BIT_IS_SET(t_module->flags, TRACK_NEW_PACKET)) + { + status = h264_new_rtp_packet(p_ctx, t_module); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + if (BIT_IS_SET(extra->flags, H264F_NEXT_PACKET_IS_START)) + { + packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + + if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) + CLEAR_BIT(extra->flags, H264F_NEXT_PACKET_IS_START); + } + + if (!extra->nal_unit_size && BITS_BYTES_AVAILABLE(p_ctx, payload)) + { + uint32_t stap_unit_header; + + /* STAP-A packet: read NAL unit size and header from payload */ + stap_unit_header = BITS_READ_U32(p_ctx, payload, 24, "STAP unit header"); + extra->nal_unit_size = stap_unit_header >> 8; + if (extra->nal_unit_size > BITS_BYTES_AVAILABLE(p_ctx, payload)) + { + LOG_ERROR(p_ctx, "H.264: STAP-A NAL unit size bigger than payload"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + extra->header_bytes_to_write = 5; + extra->nal_header = (uint8_t)stap_unit_header; + } + + header_bytes_to_write = extra->header_bytes_to_write; + size = extra->nal_unit_size + header_bytes_to_write; + + if (p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) + { + if (flags & VC_CONTAINER_READ_FLAG_INFO) + { + /* In order to set the frame end flag correctly, need to work out if this + * is the only NAL unit or last in an aggregated packet */ + last_nal_unit_in_packet = (extra->nal_unit_size == BITS_BYTES_AVAILABLE(p_ctx, payload)); + } else { + offset = 0; + data_ptr = p_packet->data; + + if (size > p_packet->buffer_size) + { + /* Buffer not big enough */ + size = p_packet->buffer_size; + } + + /* Insert start code and header into the data stream */ + while (offset < size && header_bytes_to_write) + { + uint8_t header_byte; + + switch (header_bytes_to_write) + { + case 2: header_byte = 0x01; break; + case 1: header_byte = extra->nal_header; break; + default: header_byte = 0x00; + } + data_ptr[offset++] = header_byte; + header_bytes_to_write--; + } + extra->header_bytes_to_write = header_bytes_to_write; + + if (offset < size) + { + BITS_COPY_BYTES(p_ctx, payload, size - offset, data_ptr + offset, "Packet data"); + extra->nal_unit_size -= (size - offset); + } + + /* If we've read the final bytes of the packet, this must be the last (or only) + * NAL unit in it */ + last_nal_unit_in_packet = !BITS_BYTES_AVAILABLE(p_ctx, payload); + } + p_packet->size = size; + } else { + extra->header_bytes_to_write = 0; + BITS_SKIP_BYTES(p_ctx, payload, extra->nal_unit_size, "Packet data"); + last_nal_unit_in_packet = !BITS_BYTES_AVAILABLE(p_ctx, payload); + extra->nal_unit_size = 0; + } + + /* The marker bit on an RTP packet indicates the frame ends at the end of packet */ + if (last_nal_unit_in_packet && BIT_IS_SET(t_module->flags, TRACK_HAS_MARKER)) + { + packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + + /* If this was the last packet of a frame, the next one must be the start */ + if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) + SET_BIT(extra->flags, H264F_NEXT_PACKET_IS_START); + } + + if (p_packet) + p_packet->flags = packet_flags; + + return status; +} + +/***************************************************************************** +Functions exported as part of the RTP parameter handler API + *****************************************************************************/ + +/**************************************************************************//** + * H.264 parameter handler. + * Parses the URI parameters to set up the track for an H.264 stream. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + H264_PAYLOAD_T *extra; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(params); + + /* See RFC3984, section 8.1, for parameter names and details. */ + extra = (H264_PAYLOAD_T *)malloc(sizeof(H264_PAYLOAD_T)); + if (!extra) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + track->priv->module->extra = extra; + memset(extra, 0, sizeof(H264_PAYLOAD_T)); + + /* Mandatory parameters */ + status = h264_get_sprop_parameter_sets(p_ctx, track, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Unsupported parameters */ + status = h264_check_unsupported_features(p_ctx, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Optional parameters */ + status = h264_get_packetization_mode(p_ctx, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + track->priv->module->payload_handler = h264_payload_handler; + SET_BIT(extra->flags, H264F_NEXT_PACKET_IS_START); + + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + track->priv->module->timestamp_clock = H264_TIMESTAMP_CLOCK; + + return status; +} + diff --git a/containers/rtp/rtp_h264.h b/containers/rtp/rtp_h264.h new file mode 100755 index 0000000..ffd51da --- /dev/null +++ b/containers/rtp/rtp_h264.h @@ -0,0 +1,42 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _RTP_H264_H_ +#define _RTP_H264_H_ + +#include "containers/containers.h" +#include "containers/core/containers_list.h" + +/** H.264 parameter handler + * + * \param p_ctx Container context. + * \param track Track data. + * \param params Parameter list. + * \return Status of decoding the H.264 parameters. */ +VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); + +#endif /* _RTP_H264_H_ */ diff --git a/containers/rtp/rtp_mpeg4.c b/containers/rtp/rtp_mpeg4.c new file mode 100755 index 0000000..6a968e4 --- /dev/null +++ b/containers/rtp/rtp_mpeg4.c @@ -0,0 +1,788 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#include "containers/containers.h" + +#include "containers/core/containers_logging.h" +#include "containers/core/containers_bits.h" +#include "containers/core/containers_list.h" +#include "rtp_priv.h" +#include "rtp_mpeg4.h" + +#ifdef _DEBUG +#define RTP_DEBUG 1 +#endif + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/** MPEG-4 stream types, ISO/IEC 14496-1:2010 Table 6 */ +typedef enum +{ + MPEG4_OBJECT_DESCRIPTOR_STREAM = 1, + MPEG4_CLOCK_REFERENCE_STREAM = 2, + MPEG4_SCENE_DESCRIPTION_STREAM = 3, + MPEG4_VISUAL_STREAM = 4, + MPEG4_AUDIO_STREAM = 5, + MPEG4_MPEG7_STREAM = 6, + MPEG4_IPMP_STREAM = 7, + MPEG4_OBJECT_CONTENT_INFO_STREAM = 8, + MPEG4_MPEGJ_STREAM = 9, + MPEG4_INTERACTION_STREAM = 10, + MPEG4_IPMP_TOOL_STREAM = 11, +} mp4_stream_type_t; + +/** MPEG-4 audio object types, ISO/IEC 14496-3:2009 Table 1.17 */ +typedef enum +{ + MPEG4A_AAC_MAIN = 1, + MPEG4A_AAC_LC = 2, + MPEG4A_AAC_SSR = 3, + MPEG4A_AAC_LTP = 4, + MPEG4A_SBR = 5, + MPEG4A_AAC_SCALABLE = 6, + MPEG4A_TWIN_VQ = 7, + MPEG4A_CELP = 8, + MPEG4A_HVXC = 9, + MPEG4A_TTSI = 12, + MPEG4A_MAIN_SYNTHETIC = 13, + MPEG4A_WAVETABLE_SYNTHESIS = 14, + MPEG4A_GENERAL_MIDI = 15, + MPEG4A_ALGORITHMIC_SYNTHESIS = 16, + MPEG4A_ER_AAC_LC = 17, + MPEG4A_ER_AAC_LTP = 19, + MPEG4A_ER_AAC_SCALABLE = 20, + MPEG4A_ER_TWIN_VQ = 21, + MPEG4A_ER_BSAC = 22, + MPEG4A_ER_AAC_LD = 23, + MPEG4A_ER_CELP = 24, + MPEG4A_ER_HVXC = 25, + MPEG4A_ER_HILN = 26, + MPEG4A_ER_PARAMETERIC = 27, + MPEG4A_SSC = 28, + MPEG4A_PS = 29, + MPEG4A_MPEG_SURROUND = 30, + MPEG4A_LAYER_1 = 32, + MPEG4A_LAYER_2 = 33, + MPEG4A_LAYER_3 = 34, + MPEG4A_DST = 35, + MPEG4A_ALS = 36, + MPEG4A_SLS = 37, + MPEG4A_SLS_NON_CORE = 38, + MPEG4A_ER_AAC_ELD = 39, + MPEG4A_SMR_SIMPLE = 40, + MPEG4A_SMR_MAIN = 41, +} mp4_audio_object_type_t; + +/** RTP MPEG-4 modes */ +typedef enum +{ + MP4_GENERIC_MODE = 0, + MP4_CELP_CBR_MODE, + MP4_CELP_VBR_MODE, + MP4_AAC_LBR_MODE, + MP4_AAC_HBR_MODE +} mp4_mode_t; + +typedef struct mp4_mode_detail_tag +{ + const char *name; + mp4_mode_t mode; +} MP4_MODE_ENTRY_T; + +/* RTP MPEG-4 mode look-up table. + * Note: case-insensitive sort by name */ +static MP4_MODE_ENTRY_T mp4_mode_array[] = { + { "aac-hbr", MP4_AAC_HBR_MODE }, + { "aac-lbr", MP4_AAC_LBR_MODE }, + { "celp-cbr", MP4_CELP_CBR_MODE }, + { "celp-vbr", MP4_CELP_VBR_MODE }, + { "generic", MP4_GENERIC_MODE }, +}; + +static int mp4_mode_comparator(const MP4_MODE_ENTRY_T *a, const MP4_MODE_ENTRY_T *b); + +VC_CONTAINERS_STATIC_LIST(mp4_mode_lookup, mp4_mode_array, mp4_mode_comparator); + +typedef struct au_info_tag +{ + uint32_t available; + uint32_t index; + int32_t cts_delta; + int32_t dts_delta; +} AU_INFO_T; + +typedef struct mp4_payload_tag +{ + mp4_stream_type_t stream_type; + uint32_t profile_level_id; + mp4_mode_t mode; + uint32_t size_length; + uint32_t index_length; + uint32_t index_delta_length; + uint32_t cts_delta_length; + uint32_t dts_delta_length; + uint32_t object_type; + uint32_t constant_size; + uint32_t constant_duration; + uint32_t auxiliary_length; + VC_CONTAINER_BITS_T au_headers; + AU_INFO_T au_info; +} MP4_PAYLOAD_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/**************************************************************************//** + * Convert a hexadecimal character to a value between 0 and 15. + * Upper and lower case characters are supported. An invalid chacter return zero. + * + * @param hex The character to convert. + * @return The value of the character. + */ +static uint8_t hex_to_nybble(char hex) +{ + if (hex >= '0' && hex <= '9') + return hex - '0'; + if (hex >= 'A' && hex <= 'F') + return hex - 'A' + 10; + if (hex >= 'a' && hex <= 'f') + return hex - 'a' + 10; + return 0; /* Illegal character (not hex) */ +} + +/**************************************************************************//** + * Convert a sequence of hexadecimal characters to consecutive entries in a + * byte array. + * The string must contain at least twice as many characters as the number of + * bytes to convert. + * + * @param hex The hexadecimal string. + * @param buffer The buffer into which bytes are to be stored. + * @param bytes_to_convert The number of bytes in the array to be filled. + */ +static void hex_to_byte_buffer(const char *hex, + uint8_t *buffer, + uint32_t bytes_to_convert) +{ + uint8_t value; + + while (bytes_to_convert--) + { + value = hex_to_nybble(*hex++) << 4; + value |= hex_to_nybble(*hex++); + *buffer++ = value; + } +} + +/**************************************************************************//** + * Retrieves and checks the stream type in the URI parameters. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_get_stream_type(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; + uint32_t stream_type; + VC_CONTAINER_ES_TYPE_T expected_es_type; + + if (!rtp_get_parameter_u32(params, "streamType", &stream_type)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + switch (stream_type) + { + case MPEG4_AUDIO_STREAM: + extra->stream_type = MPEG4_AUDIO_STREAM; + expected_es_type = VC_CONTAINER_ES_TYPE_AUDIO; + break; + default: + LOG_ERROR(p_ctx, "Unsupported MPEG-4 stream type: %u", stream_type); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + if (track->format->es_type != expected_es_type) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Decode and store audio configuration information from an MP4 audio + * configuration bit stream. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @param bit_stream The bit stream containing the audio configuration. + * @return True if the configuration was decoded successfully, false otherwise. + */ +static bool mp4_decode_audio_config(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_BITS_T *bit_stream) +{ + static uint32_t mp4_audio_sample_rate[] = + { 96000, 88200, 64000, 48000, 44100, 32000, 24000, + 22050, 16000, 12000, 11025, 8000, 7350, 0, 0 }; + + VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; + uint32_t audio_object_type; + uint32_t sampling_frequency_index; + uint32_t channel_configuration; + + audio_object_type = BITS_READ_U32(p_ctx, bit_stream, 5, "audioObjectType"); + if (audio_object_type == 31) + audio_object_type = BITS_READ_U32(p_ctx, bit_stream, 6, "audioObjectTypeExt") + 32; + + sampling_frequency_index = BITS_READ_U32(p_ctx, bit_stream, 4, "samplingFrequencyIndex"); + if (sampling_frequency_index == 0xF) + audio->sample_rate = BITS_READ_U32(p_ctx, bit_stream, 24, "samplingFrequency"); + else + audio->sample_rate = mp4_audio_sample_rate[sampling_frequency_index]; + if (!audio->sample_rate) return false; + + track->priv->module->timestamp_clock = audio->sample_rate; + + channel_configuration = BITS_READ_U32(p_ctx, bit_stream, 4, "channelConfiguration"); + switch (channel_configuration) + { + case 1: /* 1 channel, centre front */ + case 2: /* 2 channel, stereo front */ + case 3: /* 3 channel, centre and stereo front */ + case 4: /* 4 channel, centre and stereo front, mono surround */ + case 5: /* 5 channel, centre and stereo front, stereo surround */ + case 6: /* 5.1 channel, centre and stereo front, stereo surround, low freq */ + audio->channels = channel_configuration; + break; + case 7: /* 7.1 channel, centre, stereo and stereo outside front, stereo surround, low freq */ + audio->channels = channel_configuration + 1; + break; + default: + LOG_DEBUG(p_ctx, "MPEG-4: Unsupported channel configuration (%u)", channel_configuration); + return false; + } + + switch (audio_object_type) + { + case MPEG4A_AAC_LC: + { + uint32_t ga_specific_config = BITS_READ_U32(p_ctx, bit_stream, 3, "GASpecificConfig"); + + /* Make sure there are no unexpected (and unsupported) additional configuration elements */ + if (ga_specific_config != 0) + { + LOG_DEBUG(p_ctx, "MPEG-4: Unexpected additional configuration data (%u)", ga_specific_config); + return false; + } + } + break; + /* Add any further supported codecs here */ + default: + LOG_DEBUG(p_ctx, "MPEG-4: Unsupported Audio Object Type (%u)", audio_object_type); + return false; + } + + return true; +} + +/**************************************************************************//** + * Get, store and decode the configuration information from the URI parameters. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_get_config(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; + PARAMETER_T param; + uint32_t config_len; + VC_CONTAINER_STATUS_T status; + uint8_t *config; + VC_CONTAINER_BITS_T bit_stream; + + param.name = "config"; + if (!vc_containers_list_find_entry(params, ¶m) || !param.value) + { + LOG_ERROR(p_ctx, "MPEG-4: config parameter missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + config_len = strlen(param.value); + if (config_len & 1) + { + LOG_ERROR(p_ctx, "MPEG-4: config parameter invalid"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + config_len /= 2; + + /* Copy AudioSpecificConfig into track extradata, to be decoded by client */ + status = vc_container_track_allocate_extradata(p_ctx, track, config_len); + if(status != VC_CONTAINER_SUCCESS) return status; + + config = track->priv->extradata; + track->format->extradata_size = config_len; + hex_to_byte_buffer(param.value, config, config_len); + + /* Decode config locally, to determine sample rate, etc. */ + BITS_INIT(p_ctx, &bit_stream, config, config_len); + + switch (extra->stream_type) + { + case MPEG4_AUDIO_STREAM: + if (!mp4_decode_audio_config(p_ctx, track, &bit_stream)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + break; + default: + /* Other stream types not yet supported */ + LOG_ERROR(p_ctx, "MPEG-4: stream type %d not supported", extra->stream_type); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * MP4 mode comparison function. + * Compare two MP4 mode structures and return whether the first is less than, + * equal to or greater than the second. + * + * @param first The first structure to be compared. + * @param second The second structure to be compared. + * @return Negative if first is less than second, positive if first is greater + * and zero if they are equal. + */ +static int mp4_mode_comparator(const MP4_MODE_ENTRY_T *a, const MP4_MODE_ENTRY_T *b) +{ + return strcasecmp(a->name, b->name); +} + +/**************************************************************************//** + * Get and store the MP4 mode, if recognised, from the URI parameters. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_get_mode(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; + PARAMETER_T param; + MP4_MODE_ENTRY_T mode_entry; + + param.name = "mode"; + if (!vc_containers_list_find_entry(params, ¶m) || !param.value) + { + LOG_ERROR(p_ctx, "MPEG-4: mode parameter missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + +#ifdef RTP_DEBUG + vc_containers_list_validate(&mp4_mode_lookup); +#endif + + mode_entry.name = param.value; + if (!vc_containers_list_find_entry(&mp4_mode_lookup, &mode_entry)) + { + LOG_ERROR(p_ctx, "MPEG-4: Unrecognised mode parameter \"%s\"", mode_entry.name); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + extra->mode = mode_entry.mode; + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Check URI parameters for unsupported features. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_check_unsupported_features(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + uint32_t u32_unused; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(track); + + /* Limitation: RAP flag not yet supported */ + if (rtp_get_parameter_u32(params, "randomAccessIndication", &u32_unused)) + { + LOG_ERROR(p_ctx, "MPEG-4: random access not supported"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + /* Limitation: interleaving not yet supported */ + if (rtp_get_parameter_u32(params, "maxDisplacement", &u32_unused) || + rtp_get_parameter_u32(params, "de-interleaveBufferSize", &u32_unused)) + { + LOG_ERROR(p_ctx, "MPEG-4: interleaved packetization not supported"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + /* Limitation: system streams not supported */ + if (rtp_get_parameter_u32(params, "streamStateIndication", &u32_unused)) + { + LOG_ERROR(p_ctx, "MPEG-4: system streams not supported"); + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Validate parameters that have been read form the URI parameter list. + * + * @param p_ctx The RTP container context. + * @param track The track being constructed. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_check_parameters(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track) +{ + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra; + + switch (extra->mode) + { + case MP4_CELP_CBR_MODE: + if (!extra->constant_size) + { + LOG_ERROR(p_ctx, "MPEG-4: CELP-cbr requires constantSize parameter."); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + break; + case MP4_CELP_VBR_MODE: + case MP4_AAC_LBR_MODE: + if (extra->size_length != 6 || extra->index_length != 2 || extra->index_delta_length != 2) + { + LOG_ERROR(p_ctx, "MPEG-4: CELP-vbr/AAC-lbr invalid lengths (%u/%u/%u)", + extra->size_length, extra->index_length, extra->index_delta_length); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + break; + case MP4_AAC_HBR_MODE: + if (extra->size_length != 13 || extra->index_length != 3 || extra->index_delta_length != 3) + { + LOG_ERROR(p_ctx, "MPEG-4: AAC-hbr invalid lengths (%u/%u/%u)", + extra->size_length, extra->index_length, extra->index_delta_length); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + break; + default: /* MP4_GENERIC_MODE */ + if (extra->size_length > 32 || extra->index_length > 32 || extra->index_delta_length > 32) + { + LOG_ERROR(p_ctx, "MPEG-4: generic invalid lengths (%u/%u/%u)", + extra->size_length, extra->index_length, extra->index_delta_length); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + } + + if (extra->cts_delta_length > 32 || extra->dts_delta_length > 32) + { + LOG_ERROR(p_ctx, "MPEG-4: CTS/DTS invalid lengths (%u/%u)", + extra->cts_delta_length, extra->dts_delta_length); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Initialise payload bit stream for a new RTP packet. + * + * @param p_ctx The RTP container context. + * @param t_module The track module with the new RTP packet. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_new_rtp_packet(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module) +{ + VC_CONTAINER_BITS_T *payload = &t_module->payload; + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)t_module->extra; + VC_CONTAINER_BITS_T *au_headers = &extra->au_headers; + + /* There will be an AU header section if any of its fields are non-zero. */ + if (extra->size_length || extra->index_length || extra->cts_delta_length || extra->dts_delta_length) + { + uint32_t au_headers_length; + + /* Calculate how far to advance the payload, to get past the AU headers */ + au_headers_length = BITS_READ_U32(p_ctx, payload, 16, "AU headers length"); + au_headers_length = BITS_TO_BYTES(au_headers_length); /* Round up to bytes */ + + /* Record where the AU headers are in the payload */ + BITS_INIT(p_ctx, au_headers, BITS_CURRENT_POINTER(p_ctx, payload), au_headers_length); + BITS_SKIP_BYTES(p_ctx, &t_module->payload, au_headers_length, "Move payload past AU headers"); + } + + /* Skip the auxiliary section, if present */ + if (extra->auxiliary_length) + { + uint32_t auxiliary_data_size; + + auxiliary_data_size = BITS_READ_U32(p_ctx, payload, extra->auxiliary_length, "Auxiliary length"); + auxiliary_data_size = BITS_TO_BYTES(auxiliary_data_size); /* Round up to bytes */ + BITS_SKIP_BYTES(p_ctx, payload, auxiliary_data_size, "Auxiliary data"); + } + + return BITS_VALID(p_ctx, payload) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID; +} + +/**************************************************************************//** + * Read a flagged delta from an AU header bit stream. + * A flagged delta is an optional value in the stream that is preceded by a + * flag bit that indicates whether the value is present in the stream. If the + * length of the value is zero bits, the flag is never present. + * + * @pre The delta_length must be 32 or less. + * + * @param p_ctx The container context. + * @param au_headers The AU header bit stream. + * @param delta_length The number of bits in the delta value. + * @return The delta value, or zero if not present. + */ +static int32_t mp4_flagged_delta(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_BITS_T *au_headers, + uint32_t delta_length) +{ + uint32_t value = 0; + + /* Flag is only present if the delta length is non-zero */ + if (delta_length && BITS_READ_U32(p_ctx, au_headers, 1, "CTS/DTS delta present")) + { + value = BITS_READ_U32(p_ctx, au_headers, delta_length, "CTS/DTS delta"); + + /* Sign extend value based on bit length */ + if (value & (1 << (delta_length - 1))) + value |= ~((1 << delta_length) - 1); + } + + return (int32_t)value; +} + +/**************************************************************************//** + * Read next AU header from the bit stream. + * + * @param p_ctx The RTP container context. + * @param extra The MP4-specific track module information. + * @param is_first_au True if the first AU header in the packet is being read. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_next_au_header(VC_CONTAINER_T *p_ctx, + MP4_PAYLOAD_T *extra, + bool is_first_au) +{ + VC_CONTAINER_BITS_T *au_headers = &extra->au_headers; + AU_INFO_T *au_info = &extra->au_info; + + /* See RFC3550 section 3.2.1.1 */ + + if (extra->constant_size) + au_info->available = extra->constant_size; + else + au_info->available = BITS_READ_U32(p_ctx, au_headers, extra->size_length, "AU size"); + + if (is_first_au) + au_info->index = BITS_READ_U32(p_ctx, au_headers, extra->index_length, "AU index"); + else + au_info->index += BITS_READ_U32(p_ctx, au_headers, extra->index_delta_length, "AU index delta") + 1; + + au_info->cts_delta = mp4_flagged_delta(p_ctx, au_headers, extra->cts_delta_length); + au_info->dts_delta = mp4_flagged_delta(p_ctx, au_headers, extra->dts_delta_length); + + /* RAP and stream state not supported yet */ + + return BITS_VALID(p_ctx, au_headers) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID; +} + +/**************************************************************************//** + * MP4 payload handler. + * Extracts/skips data from the payload according to the AU headers. + * + * @param p_ctx The RTP container context. + * @param track The track being read. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T mp4_payload_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags) +{ + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + VC_CONTAINER_BITS_T *payload = &t_module->payload; + MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)t_module->extra; + AU_INFO_T *au_info = &extra->au_info; + bool is_new_packet = BIT_IS_SET(t_module->flags, TRACK_NEW_PACKET); + uint32_t bytes_left_in_payload; + uint32_t size; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if (is_new_packet) + { + status = mp4_new_rtp_packet(p_ctx, t_module); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + if (!au_info->available) + { + status = mp4_next_au_header(p_ctx, extra, is_new_packet); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + if (p_packet) + { + /* Adjust the packet time stamps using deltas */ + p_packet->pts += au_info->cts_delta; + p_packet->dts += au_info->dts_delta; + } + + size = au_info->available; + bytes_left_in_payload = BITS_BYTES_AVAILABLE(p_ctx, payload); + if (size > bytes_left_in_payload) + { + /* AU is fragmented across RTP packets */ + size = bytes_left_in_payload; + } + + if (p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP)) + { + if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) + { + if (size > p_packet->buffer_size) + size = p_packet->buffer_size; + + BITS_COPY_BYTES(p_ctx, payload, size, p_packet->data, "Packet data"); + } + p_packet->size = size; + } else { + BITS_SKIP_BYTES(p_ctx, payload, size, "Packet data"); + } + + if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) + au_info->available -= size; + + return BITS_VALID(p_ctx, payload) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID; +} + +/***************************************************************************** +Functions exported as part of the RTP parameter handler API + *****************************************************************************/ + +/**************************************************************************//** + * MP4 parameter handler. + * Parses the URI parameters to set up the track for an MP4 stream. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + MP4_PAYLOAD_T *extra; + VC_CONTAINER_STATUS_T status; + + /* See RFC3640, section 4.1, for parameter names and details. */ + extra = (MP4_PAYLOAD_T *)malloc(sizeof(MP4_PAYLOAD_T)); + if (!extra) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + track->priv->module->extra = extra; + memset(extra, 0, sizeof(MP4_PAYLOAD_T)); + + /* Mandatory parameters */ + status = mp4_get_stream_type(p_ctx, track, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_get_config(p_ctx, track, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + status = mp4_get_mode(p_ctx, track, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Unsupported parameters */ + status = mp4_check_unsupported_features(p_ctx, track, params); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Optional parameters */ + rtp_get_parameter_u32(params, "sizeLength", &extra->size_length); + rtp_get_parameter_u32(params, "indexLength", &extra->index_length); + rtp_get_parameter_u32(params, "indexDeltaLength", &extra->index_delta_length); + rtp_get_parameter_u32(params, "CTSDeltaLength", &extra->cts_delta_length); + rtp_get_parameter_u32(params, "DTSDeltaLength", &extra->dts_delta_length); + rtp_get_parameter_u32(params, "objectType", &extra->object_type); + rtp_get_parameter_u32(params, "constantSize", &extra->constant_size); + rtp_get_parameter_u32(params, "constantDuration", &extra->constant_duration); + rtp_get_parameter_u32(params, "auxiliaryDataSizeLength", &extra->auxiliary_length); + + if (extra->constant_size && extra->size_length) + { + LOG_ERROR(p_ctx, "MPEG4: constantSize and sizeLength cannot both be set."); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + status = mp4_check_parameters(p_ctx, track); + if (status != VC_CONTAINER_SUCCESS) return status; + + track->priv->module->payload_handler = mp4_payload_handler; + + return VC_CONTAINER_SUCCESS; +} diff --git a/containers/rtp/rtp_mpeg4.h b/containers/rtp/rtp_mpeg4.h new file mode 100755 index 0000000..99181bb --- /dev/null +++ b/containers/rtp/rtp_mpeg4.h @@ -0,0 +1,42 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _RTP_MPEG4_H_ +#define _RTP_MPEG4_H_ + +#include "containers/containers.h" +#include "containers/core/containers_list.h" + +/** MPEG-4 parameter handler + * + * \param p_ctx Container context. + * \param track Track data. + * \param params Parameter list. + * \return Status of decoding the MPEG-4 parameters. */ +VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); + +#endif /* _RTP_MPEG4_H_ */ diff --git a/containers/rtp/rtp_priv.h b/containers/rtp/rtp_priv.h new file mode 100755 index 0000000..c54bfe8 --- /dev/null +++ b/containers/rtp/rtp_priv.h @@ -0,0 +1,111 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _RTP_PRIV_H_ +#define _RTP_PRIV_H_ + +#include "containers/containers.h" + +#include "containers/core/containers_private.h" +#include "containers/core/containers_bits.h" +#include "containers/core/containers_list.h" + +typedef VC_CONTAINER_STATUS_T (*PAYLOAD_HANDLER_T)(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags); + +/** Parameter list entry type. */ +typedef struct parameter_tag +{ + const char *name; + const char *value; +} PARAMETER_T; + +/** Prototype for MIME parameter handling. + * Each MIME type has a certain set of parameter names it uses, so a handler is + * needed for each type. This is that handler's prototype. + */ +typedef VC_CONTAINER_STATUS_T (*PARAMETER_HANDLER_T)(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); + +/** Track module flag bit numbers (up to seven) */ +typedef enum +{ + TRACK_SSRC_SET = 0, + TRACK_HAS_MARKER, + TRACK_NEW_PACKET, +} track_module_flag_bit_t; + +/** RTP track data */ +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + PAYLOAD_HANDLER_T payload_handler; /**< Extracts the data from the payload */ + uint8_t *buffer; /**< Buffer into which the RTP packet is read */ + VC_CONTAINER_BITS_T payload; /**< Payload bit bit_stream */ + uint8_t flags; /**< Combination of track module flags */ + uint8_t payload_type; /**< The expected payload type */ + uint16_t max_seq_num; /**< Highest seq. number seen */ + uint32_t timestamp; /**< RTP timestamp of packet */ + uint32_t timestamp_base; /**< RTP timestamp value that equates to time zero */ + uint32_t last_timestamp_top; /**< Top two bits of RTP timestamp of previous packet */ + uint32_t timestamp_wraps; /**< Count of the times that the timestamp has wrapped */ + uint32_t timestamp_clock; /**< Clock frequency of RTP timestamp values */ + uint32_t expected_ssrc; /**< The expected SSRC, if set */ + uint32_t base_seq; /**< Base seq number */ + uint32_t bad_seq; /**< Last 'bad' seq number + 1 */ + uint32_t probation; /**< Sequential packets till source is valid */ + uint32_t received; /**< RTP packets received */ + void *extra; /**< Payload specific data */ +} VC_CONTAINER_TRACK_MODULE_T; + +/** Determine minimum number of bytes needed to hold a number of bits */ +#define BITS_TO_BYTES(X) (((X) + 7) >> 3) + +/** Collection of bit manipulation routines */ +/* @{ */ +#define SET_BIT(V, B) V |= (1 << (B)) +#define CLEAR_BIT(V, B) V &= ~(1 << (B)) +#define BIT_IS_SET(V, B) (!(!((V) & (1 << (B))))) +#define BIT_IS_CLEAR(V, B) (!((V) & (1 << (B)))) +/* }@ */ + + +/** Get a parameter's value as a decimal number. + * + * \param param_list The list of parameter name/value pairs. + * \param name The paramter's name. + * \param value Where to put the converted value. + * \return True if successful, false if the parameter was not found or didn't convert. */ +bool rtp_get_parameter_u32(const VC_CONTAINERS_LIST_T *param_list, const char *name, uint32_t *value); + +/** Get a parameter's value as a hexadecimal number. + * + * \param param_list The list of parameter name/value pairs. + * \param name The paramter's name. + * \param value Where to put the converted value. + * \return True if successful, false if the parameter was not found or didn't convert. */ +bool rtp_get_parameter_x32(const VC_CONTAINERS_LIST_T *param_list, const char *name, uint32_t *value); + +#endif /* _RTP_PRIV_H_ */ diff --git a/containers/rtp/rtp_reader.c b/containers/rtp/rtp_reader.c new file mode 100755 index 0000000..9ce4a59 --- /dev/null +++ b/containers/rtp/rtp_reader.c @@ -0,0 +1,1158 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_uri.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_bits.h" +#include "containers/core/containers_list.h" + +#include "rtp_priv.h" +#include "rtp_mpeg4.h" +#include "rtp_h264.h" + +#ifdef _DEBUG +/* Validates static sorted lists are correctly constructed */ +#define RTP_DEBUG 1 +#endif + +/****************************************************************************** +Configurable defines and constants. +******************************************************************************/ + +/** Maximum size of an RTP packet */ +#define MAXIMUM_PACKET_SIZE 2048 + +/** Maximum number of RTP packets that can be missed without restarting. */ +#define MAX_DROPOUT 3000 +/** Maximum number of out of sequence RTP packets that are accepted. */ +#define MAX_MISORDER 0 +/** Minimum number of sequential packets required for an acceptable connection + * when restarting. */ +#define MIN_SEQUENTIAL 2 + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +#define RTP_SCHEME "rtp:" + +/** The RTP PKT scheme is used with test pkt files */ +#define RTP_PKT_SCHEME "rtppkt:" + +/** \name RTP URI parameter names + * @{ */ +#define PAYLOAD_TYPE_NAME "rtppt" +#define MIME_TYPE_NAME "mime-type" +#define CHANNELS_NAME "channels" +#define RATE_NAME "rate" +#define SSRC_NAME "ssrc" +#define SEQ_NAME "seq" +/* @} */ + +/** A sentinel codec that is not supported */ +#define UNSUPPORTED_CODEC VC_FOURCC(0,0,0,0) + +/** Evaluates to true if the given payload type is in the supported static audio range. */ +#define IS_STATIC_AUDIO_TYPE(PT) ((PT) < countof(static_audio_payload_types)) + +/** Payload type number for the first static video type */ +#define FIRST_STATIC_VIDEO_TYPE 24 +/** Evaluates to true if the given payload type is in the supported static video range. */ +#define IS_STATIC_VIDEO_TYPE(PT) ((PT) >= FIRST_STATIC_VIDEO_TYPE && \ + (PT) < (FIRST_STATIC_VIDEO_TYPE + countof(static_video_payload_types))) + +/** Evaluates to true if the given payload type is in the dynamic range. */ +#define IS_DYNAMIC_TYPE(PT) ((PT) >= 96 && (PT) < 128) + +/** All sequence numbers are modulo this value. */ +#define RTP_SEQ_MOD (1 << 16) + +/** All the static video payload types use a 90kHz timestamp clock */ +#define STATIC_VIDEO_TIMESTAMP_CLOCK 90000 + +/** Number of microseconds in a second, used to convert RTP timestamps to microseconds */ +#define MICROSECONDS_PER_SECOND 1000000 + +/****************************************************************************** +Type definitions +******************************************************************************/ + +/** \name MIME type parameter handlers + * Function prototypes for payload parameter handlers */ +/* @{ */ +static VC_CONTAINER_STATUS_T audio_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); +static VC_CONTAINER_STATUS_T l8_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); +static VC_CONTAINER_STATUS_T l16_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); +/* @} */ + +/** \name MIME type payload handlers */ +/* @{ */ +static VC_CONTAINER_STATUS_T l16_payload_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags); +/* @} */ + +/** Static audio payload type data */ +typedef struct audio_payload_type_data_tag +{ + VC_CONTAINER_FOURCC_T codec; /**< FourCC codec for this payload type */ + uint32_t channels; /**< Number of audio channels */ + uint32_t sample_rate; /**< Sample rate */ + uint32_t bits_per_sample; /**< Bits per sample, or 1 if not applicable */ + PARAMETER_HANDLER_T param_handler; /**< Optional parameter handler */ + PAYLOAD_HANDLER_T payload_handler; /**< Optional payload handler */ +} AUDIO_PAYLOAD_TYPE_DATA_T; + +/** The details for the statically defined audio payload types from RFC3551 */ +static AUDIO_PAYLOAD_TYPE_DATA_T static_audio_payload_types[] = +{ + { VC_CONTAINER_CODEC_MULAW, 1, 8000, 8, audio_parameter_handler, NULL }, /* 0 - PCMU */ + { UNSUPPORTED_CODEC }, /* 1 - reserved */ + { UNSUPPORTED_CODEC }, /* 2 - reserved */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 3 - GSM */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 4 - G723 */ + { UNSUPPORTED_CODEC, 1, 8000, 4, NULL, NULL }, /* 5 - DVI4 */ + { UNSUPPORTED_CODEC, 1, 16000, 4, NULL, NULL }, /* 6 - DVI4 */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 7 - LPC */ + { VC_CONTAINER_CODEC_ALAW, 1, 8000, 8, audio_parameter_handler, NULL }, /* 8 - PCMA */ + { UNSUPPORTED_CODEC, 1, 8000, 8, NULL, NULL }, /* 9 - G722 */ + { VC_CONTAINER_CODEC_PCM_SIGNED, 2, 44100, 16, audio_parameter_handler, l16_payload_handler }, /* 10 - L16 */ + { VC_CONTAINER_CODEC_PCM_SIGNED, 1, 44100, 16, audio_parameter_handler, l16_payload_handler }, /* 11 - L16 */ + { VC_CONTAINER_CODEC_QCELP, 1, 8000, 16, NULL, NULL }, /* 12 - QCELP */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 13 - CN */ + { VC_CONTAINER_CODEC_MPGA, 1, 90000, 1, NULL, NULL }, /* 14 - MPA */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 15 - G728 */ + { UNSUPPORTED_CODEC, 1, 11025, 4, NULL, NULL }, /* 16 - DVI4 */ + { UNSUPPORTED_CODEC, 1, 22050, 4, NULL, NULL }, /* 17 - DVI4 */ + { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 18 - G729 */ +}; + +/** Static video payload type data */ +typedef struct video_payload_type_data_tag +{ + VC_CONTAINER_FOURCC_T codec; /**< FourCC codec for this payload type */ + PARAMETER_HANDLER_T param_handler; /**< Optional parameter handler */ + PAYLOAD_HANDLER_T payload_handler; /**< Optional payload handler */ +} VIDEO_PAYLOAD_TYPE_DATA_T; + +/** The details for the statically defined video payload types from RFC3551 */ +static VIDEO_PAYLOAD_TYPE_DATA_T static_video_payload_types[] = +{ + { UNSUPPORTED_CODEC }, /* 24 - unassigned */ + { UNSUPPORTED_CODEC }, /* 25 - CelB */ + { UNSUPPORTED_CODEC }, /* 26 - JPEG */ + { UNSUPPORTED_CODEC }, /* 27 - unassigned */ + { UNSUPPORTED_CODEC }, /* 28 - nv */ + { UNSUPPORTED_CODEC }, /* 29 - unassigned */ + { UNSUPPORTED_CODEC }, /* 30 - unassigned */ + { UNSUPPORTED_CODEC }, /* 31 - H261 */ + { VC_CONTAINER_CODEC_MP2V, NULL, NULL }, /* 32 - MPV */ + { UNSUPPORTED_CODEC }, /* 33 - MP2T */ + { VC_CONTAINER_CODEC_H263, NULL, NULL } /* 34 - H263 */ +}; + +/** MIME type details */ +typedef struct mime_type_data_tag +{ + const char *name; /**< Name of MIME type */ + VC_CONTAINER_ES_TYPE_T es_type; /**< Elementary stream type */ + VC_CONTAINER_FOURCC_T codec; /**< Codec to be used */ + PARAMETER_HANDLER_T param_handler; /**< Parameter handler for this MIME type */ +} MIME_TYPE_DATA_T; + +/** Comparator for MIME type details. */ +static int mime_type_data_comparator(const MIME_TYPE_DATA_T *a, const MIME_TYPE_DATA_T *b); + +/** Dynamic audio payload details + * Note: case-insensitive sort by name */ +static MIME_TYPE_DATA_T dynamic_mime_details[] = { + { "audio/l16", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_PCM_SIGNED, l16_parameter_handler }, + { "audio/l8", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_PCM_SIGNED, l8_parameter_handler }, + { "audio/mpeg4-generic", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A, mp4_parameter_handler }, + { "video/h264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264, h264_parameter_handler }, + { "video/mpeg4-generic", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V, mp4_parameter_handler }, +}; + +/** Sorted list of dynamic MIME type details */ +VC_CONTAINERS_STATIC_LIST(dynamic_mime, dynamic_mime_details, mime_type_data_comparator); + +/** RTP reader data. */ +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/**************************************************************************//** + * Parameter comparison function. + * Compare two parameter structures and return whether the first is less than, + * equal to or greater than the second. + * + * @param first The first structure to be compared. + * @param second The second structure to be compared. + * @return Negative if first is less than second, positive if first is greater + * and zero if they are equal. + */ +static int parameter_comparator(const PARAMETER_T *first, const PARAMETER_T *second) +{ + return strcasecmp(first->name, second->name); +} + +/**************************************************************************//** + * Creates and populates a parameter list from a URI structure. + * The list does not copy the parameter strings themselves, so the URI structure + * must be retained (and its parameters unmodified) while the list is in use. + * + * @param uri The URI containing the parameters. + * @return List created from the parameters of the URI, or NULL on error. + */ +static VC_CONTAINERS_LIST_T *fill_parameter_list(VC_URI_PARTS_T *uri) +{ + uint32_t num_parameters = vc_uri_num_queries(uri); + VC_CONTAINERS_LIST_T *parameters; + uint32_t ii; + + parameters = vc_containers_list_create(num_parameters, sizeof(PARAMETER_T), (VC_CONTAINERS_LIST_COMPARATOR_T)parameter_comparator); + if (!parameters) + return NULL; + + for (ii = 0; ii < num_parameters; ii++) + { + PARAMETER_T param; + + vc_uri_query(uri, ii, ¶m.name, ¶m.value); + if (!vc_containers_list_insert(parameters, ¶m, false)) + { + vc_containers_list_destroy(parameters); + return NULL; + } + } + +#ifdef RTP_DEBUG + vc_containers_list_validate(parameters); +#endif + + return parameters; +} + +/**************************************************************************//** + * Decodes a static audio payload type into track information. + * The static parameters may be overridden by URI parameters. + * + * @param p_ctx The reader context. + * @param track The track to be populated. + * @param param_list The URI parameter list. + * @param payload_type The static payload type. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T decode_static_audio_type(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *param_list, + uint32_t payload_type) +{ + VC_CONTAINER_ES_FORMAT_T *format = track->format; + AUDIO_PAYLOAD_TYPE_DATA_T *data = &static_audio_payload_types[payload_type]; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(param_list); + + vc_container_assert(payload_type < countof(static_audio_payload_types)); + + if (data->codec == UNSUPPORTED_CODEC) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + format->codec = data->codec; + format->type->audio.channels = data->channels; + format->type->audio.sample_rate = data->sample_rate; + format->type->audio.bits_per_sample = data->bits_per_sample; + format->type->audio.block_align = data->channels * BITS_TO_BYTES(data->bits_per_sample); + track->priv->module->timestamp_clock = format->type->audio.sample_rate; + track->priv->module->payload_handler = data->payload_handler; + + if (data->param_handler) + data->param_handler(p_ctx, track, param_list); + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Decodes a static video payload type into track information. + * The static parameters may be overridden by URI parameters. + * + * @param p_ctx The reader context. + * @param track The track to be populated. + * @param param_list The URI parameter list. + * @param payload_type The static payload type. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T decode_static_video_type(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *param_list, + uint32_t payload_type) +{ + VC_CONTAINER_ES_FORMAT_T *format = track->format; + VIDEO_PAYLOAD_TYPE_DATA_T *data = &static_video_payload_types[payload_type - FIRST_STATIC_VIDEO_TYPE]; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(param_list); + + vc_container_assert(payload_type >= FIRST_STATIC_VIDEO_TYPE); + vc_container_assert(payload_type < FIRST_STATIC_VIDEO_TYPE + countof(static_video_payload_types)); + + if (data->codec == UNSUPPORTED_CODEC) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + format->codec = data->codec; + track->priv->module->timestamp_clock = STATIC_VIDEO_TIMESTAMP_CLOCK; + track->priv->module->payload_handler = data->payload_handler; + + if (data->param_handler) + data->param_handler(p_ctx, track, param_list); + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Compare two MIME type structures and return whether the first is less than, + * equal to or greater than the second. + * + * @param a The first parameter structure to be compared. + * @param b The second parameter structure to be compared. + * @return Negative if a is less than b, positive if a is greater and zero if + * they are equal. + */ +static int mime_type_data_comparator(const MIME_TYPE_DATA_T *a, const MIME_TYPE_DATA_T *b) +{ + return strcasecmp(a->name, b->name); +} + +/**************************************************************************//** + * Generic audio parameter handler. + * Updates the track information with generic audio parameters, such as "rate" + * and "channels". + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T audio_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + /* See RFC3555. Generic audio parameters that can override static payload + * type defaults. */ + if (rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) + track->priv->module->timestamp_clock = audio->sample_rate; + if (rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) + audio->block_align = audio->channels * BITS_TO_BYTES(audio->bits_per_sample); + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * L8 specific audio parameter handler. + * Updates the track information with audio parameters needed by the audio/L8 + * MIME type. For example, the "rate" parameter is mandatory. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T l8_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + /* See RFC3555, section 4.1.14, for parameter names and details. */ + if (!rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + if (!rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) + audio->channels = 1; + audio->bits_per_sample = 8; + audio->block_align = audio->channels; + track->priv->module->timestamp_clock = audio->sample_rate; + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * L16 specific audio parameter handler. + * Updates the track information with audio parameters needed by the audio/L16 + * MIME type. For example, the "rate" parameter is mandatory. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T l16_parameter_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *params) +{ + VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + /* See RFC3555, section 4.1.15, for parameter names and details. */ + if (!rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + if (!rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) + audio->channels = 1; + audio->bits_per_sample = 16; + audio->block_align = audio->channels * 2; + track->priv->module->timestamp_clock = audio->sample_rate; + track->priv->module->payload_handler = l16_payload_handler; + + /* TODO: add support for "channel-order" to set channel mapping */ + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Decode a dynamic payload type from parameters. + * Populates the track information with data for supported dynamic media types. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param param_list The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T decode_dynamic_type(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *param_list) +{ + VC_CONTAINER_ES_FORMAT_T *format = track->format; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + PARAMETER_T mime_type; + MIME_TYPE_DATA_T mime_details; + + /* Get MIME type parameter */ + mime_type.name = MIME_TYPE_NAME; + if (!vc_containers_list_find_entry(param_list, &mime_type)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + +#ifdef RTP_DEBUG + vc_containers_list_validate(&dynamic_mime); +#endif + + /* Look up MIME type to see if it can be handled */ + mime_details.name = mime_type.value; + if (!vc_containers_list_find_entry(&dynamic_mime, &mime_details)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + format->codec = mime_details.codec; + format->es_type = mime_details.es_type; + + /* Default number of channels for audio is one */ + if (mime_details.es_type == VC_CONTAINER_ES_TYPE_AUDIO) + format->type->audio.channels = 1; + + /* Lete MIME type specific parameter handler deal with any other parameters */ + status = mime_details.param_handler(p_ctx, track, param_list); + + /* Ensure that the sample rate has been set for audio formats */ + if (mime_details.es_type == VC_CONTAINER_ES_TYPE_AUDIO && !format->type->audio.sample_rate) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + return status; +} + +/**************************************************************************//** + * Decode the RTP payload type. + * Populates track information with data from static tables and the URI + * parameter list, according to the payload and MIME types. + * + * @param p_ctx The reader context. + * @param track The track to be updated. + * @param params The URI parameter list. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T decode_payload_type(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + const VC_CONTAINERS_LIST_T *param_list, + uint32_t payload_type) +{ + VC_CONTAINER_TRACK_MODULE_T *module = track->priv->module; + VC_CONTAINER_STATUS_T status; + + if (IS_STATIC_AUDIO_TYPE(payload_type)) + status = decode_static_audio_type(p_ctx, track, param_list, payload_type); + else if (IS_STATIC_VIDEO_TYPE(payload_type)) + status = decode_static_video_type(p_ctx, track, param_list, payload_type); + else if (IS_DYNAMIC_TYPE(payload_type)) + status = decode_dynamic_type(p_ctx, track, param_list); + else + status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + module->payload_type = (uint8_t)payload_type; + + return status; +} + +/**************************************************************************//** + * Initialises the RTP sequence number algorithm with a new sequence number. + * + * @param t_module The track module. + * @param seq The new sequence number. + */ +static void init_sequence_number(VC_CONTAINER_TRACK_MODULE_T *t_module, + uint16_t seq) +{ + t_module->base_seq = seq; + t_module->max_seq_num = seq; + t_module->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */ + t_module->received = 0; +} + +/**************************************************************************//** + * Checks whether the sequence number for a packet is acceptable or not. + * The packet will be unacceptable if it is out of sequence by some degree, or + * if the packet sequence is still being established. + * + * @param t_module The track module. + * @param seq The new sequence number. + * @return True if the sequence number indicates the packet is acceptable + */ +static bool update_sequence_number(VC_CONTAINER_TRACK_MODULE_T *t_module, + uint16_t seq) +{ + uint16_t udelta = seq - t_module->max_seq_num; + + /* NOTE: This source is derived from the example code in RFC3550, section A.1 */ + + /* Source is not valid until MIN_SEQUENTIAL packets with + * sequential sequence numbers have been received. */ + if (t_module->probation) + { + /* packet is in sequence */ + if (seq == t_module->max_seq_num + 1) + { + t_module->probation--; + t_module->max_seq_num = seq; + LOG_INFO(0, "RTP: Probation, %u more packet(s) to go at 0x%4.4hx", t_module->probation, seq); + + if (!t_module->probation) + { + init_sequence_number(t_module, seq); + t_module->received++; + return 1; + } + } else { + t_module->probation = MIN_SEQUENTIAL - 1; + t_module->max_seq_num = seq; + LOG_INFO(0, "RTP: Probation reset, wait for %u packet(s) at 0x%4.4hx", t_module->probation, seq); + } + return 0; + } else if (udelta < MAX_DROPOUT) + { + if (!udelta) + { + /* Duplicate packet, drop it */ + LOG_INFO(0, "RTP: Drop duplicate packet at 0x%4.4hx", seq); + return 0; + } + if (udelta > 1) + { + LOG_INFO(0, "RTP: Jumped by %hu packets to 0x%4.4hx", udelta, seq); + } + /* in order, with permissible gap */ + t_module->max_seq_num = seq; + } else +#if (MAX_MISORDER != 0) + /* When MAX_MISORDER is zero, always treat as out of order */ + if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) +#endif + { + /* the sequence number made a very large jump */ + if (seq == t_module->bad_seq) + { + LOG_INFO(0, "RTP: Misorder restart at 0x%4.4hx", seq); + /* Two sequential packets -- assume that the other side + * restarted without telling us so just re-sync + * (i.e., pretend this was the first packet). */ + init_sequence_number(t_module, seq); + } else { + LOG_INFO(0, "RTP: Misorder at 0x%4.4hx, expected 0x%4.4hx", seq, t_module->max_seq_num); + t_module->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1); + return 0; + } + } +#if (MAX_MISORDER != 0) + else { + /* duplicate or reordered packet */ + + /* TODO: handle out of order packets */ + } +#endif + t_module->received++; + return 1; +} + +/**************************************************************************//** + * Extract the fields of an RTP packet and validate it. + * + * @param p_ctx The reader context. + * @param t_module The track module. + * @return True if successful, false if there were not enough bits in the + * packet or the packet was invalid. + */ +static void decode_rtp_packet_header(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module) +{ + VC_CONTAINER_BITS_T *payload = &t_module->payload; + uint32_t version, has_padding, has_extension, csrc_count, has_marker; + uint32_t payload_type, ssrc; + uint16_t seq_num; + + /* Break down fixed header area into component parts */ + version = BITS_READ_U32(p_ctx, payload, 2, "Version"); + has_padding = BITS_READ_U32(p_ctx, payload, 1, "Has padding"); + has_extension = BITS_READ_U32(p_ctx, payload, 1, "Has extension"); + csrc_count = BITS_READ_U32(p_ctx, payload, 4, "CSRC count"); + has_marker = BITS_READ_U32(p_ctx, payload, 1, "Has marker"); + payload_type = BITS_READ_U32(p_ctx, payload, 7, "Payload type"); + seq_num = BITS_READ_U16(p_ctx, payload, 16, "Sequence number"); + t_module->timestamp = BITS_READ_U32(p_ctx, payload, 32, "Timestamp"); + ssrc = BITS_READ_U32(p_ctx, payload, 32, "SSRC"); + + /* If there was only a partial header, abort immediately */ + if (!BITS_VALID(p_ctx, payload)) + return; + + /* Validate version, payload type, sequence number and SSRC, if set */ + if (version != 2 || payload_type != t_module->payload_type) + { + BITS_INVALIDATE(p_ctx, payload); + return; + } + if (BIT_IS_SET(t_module->flags, TRACK_SSRC_SET) && (ssrc != t_module->expected_ssrc)) + { + LOG_DEBUG(p_ctx, "RTP: Unexpected SSRC (0x%8.8X)", ssrc); + BITS_INVALIDATE(p_ctx, payload); + return; + } + + /* Check sequence number indicates packet is usable */ + if (!update_sequence_number(t_module, seq_num)) + { + BITS_INVALIDATE(p_ctx, payload); + return; + } + + /* Adjust to account for padding, CSRCs and extension */ + if (has_padding) + { + VC_CONTAINER_BITS_T bit_stream; + uint32_t available = BITS_BYTES_AVAILABLE(p_ctx, payload); + uint8_t padding; + + BITS_COPY_STREAM(p_ctx, &bit_stream, payload); + /* The last byte of the payload is the number of padding bytes, including itself */ + BITS_SKIP_BYTES(p_ctx, &bit_stream, available - 1, "Skip to padding length"); + padding = BITS_READ_U8(p_ctx, &bit_stream, 8, "Padding length"); + + BITS_REDUCE_BYTES(p_ctx, payload, padding, "Remove padding"); + } + + /* Each CSRC is 32 bits, so shift count up to skip the right number of bits */ + BITS_SKIP(p_ctx, payload, csrc_count << 5, "CSRC section"); + + if (has_extension) + { + uint32_t extension_bits; + + /* Extension header is 16-bit ID (which isn't needed), then 16-bit length in 32-bit words */ + BITS_SKIP(p_ctx, payload, 16, "Extension ID"); + extension_bits = BITS_READ_U32(p_ctx, payload, 16, "Extension length") << 5; + BITS_SKIP(p_ctx, payload, extension_bits, "Extension data"); + } + + /* Record whether or not this RTP packet had the marker bit set */ + if (has_marker) + SET_BIT(t_module->flags, TRACK_HAS_MARKER); + else + CLEAR_BIT(t_module->flags, TRACK_HAS_MARKER); + + /* If it hasn't been set independently, use the first timestamp as a baseline */ + if (!t_module->timestamp_base) + t_module->timestamp_base = t_module->timestamp; + t_module->timestamp -= t_module->timestamp_base; +} + +/**************************************************************************//** + * Generic payload handler. + * Copies/skips data verbatim from the packet payload. + * + * @param p_ctx The reader context. + * @param track The track being read. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T generic_payload_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags) +{ + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + VC_CONTAINER_BITS_T *payload = &t_module->payload; + uint32_t size; + + VC_CONTAINER_PARAM_UNUSED(p_ctx); + + if (!p_packet) + { + /* Skip the rest of this RTP packet */ + BITS_INVALIDATE(p_ctx, payload); + return VC_CONTAINER_SUCCESS; + } + + /* Copy as much as possible into the client packet buffer */ + size = BITS_BYTES_AVAILABLE(p_ctx, payload); + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + BITS_SKIP_BYTES(p_ctx, payload, size, "Packet data"); + else { + if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) + { + if (size > p_packet->buffer_size) + size = p_packet->buffer_size; + + BITS_COPY_BYTES(p_ctx, payload, size, p_packet->data, "Packet data"); + } + p_packet->size = size; + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * L16 payload handler. + * Copies/skips data from the packet payload. On copy, swaps consecutive bytes + * in the data in order to get expected byte order. + * + * @param p_ctx The reader context. + * @param track The track being read. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T l16_payload_handler(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags) +{ + VC_CONTAINER_STATUS_T status; + + /* Most aspects are handled adequately by the generic handler */ + status = generic_payload_handler(p_ctx, track, p_packet, flags); + if (status != VC_CONTAINER_SUCCESS) + return status; + + if (p_packet && !(flags & (VC_CONTAINER_READ_FLAG_SKIP | VC_CONTAINER_READ_FLAG_INFO))) + { + uint8_t *ptr, *end_ptr; + + /* Ensure packet size is even */ + p_packet->size &= ~1; + + /* Swap bytes of each sample, to get host order instead of network order */ + for (ptr = p_packet->data, end_ptr = ptr + p_packet->size; ptr < end_ptr; ptr += 2) + { + uint8_t high_byte = ptr[0]; + ptr[0] = ptr[1]; + ptr[1] = high_byte; + } + } + + return status; +} + + +/***************************************************************************** +Utility functions for use by RTP payload handlers + *****************************************************************************/ + +/**************************************************************************//** + * Gets the value of a parameter as an unsigned 32-bit decimal integer. + * + * @param param_list The URI parameter list. + * @param name The name of the parameter. + * @param value Pointer to the variable to receive the value. + * @return True if the parameter value was read and stored correctly, false + * otherwise. + */ +bool rtp_get_parameter_u32(const VC_CONTAINERS_LIST_T *param_list, + const char *name, + uint32_t *value) +{ + PARAMETER_T param; + + param.name = name; + if (vc_containers_list_find_entry(param_list, ¶m) && param.value) + { + char *end; + + *value = strtoul(param.value, &end, 10); + return (end != param.value) && (*end == '\0'); + } + + return false; +} + +/**************************************************************************//** + * Gets the value of a parameter as an unsigned 32-bit hexadecimal integer. + * + * @param param_list The URI parameter list. + * @param name The name of the parameter. + * @param value Pointer to the variable to receive the value. + * @return True if the parameter value was read and stored correctly, false + * otherwise. + */ +bool rtp_get_parameter_x32(const VC_CONTAINERS_LIST_T *param_list, + const char *name, + uint32_t *value) +{ + PARAMETER_T param; + + param.name = name; + if (vc_containers_list_find_entry(param_list, ¶m) && param.value) + { + char *end; + + *value = strtoul(param.value, &end, 16); + return (end != param.value) && (*end == '\0'); + } + + return false; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +/**************************************************************************//** + * Read/skip data from the container. + * Can also be used to query information about the next block of data. + * + * @param p_ctx The reader context. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtp_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags ) +{ + VC_CONTAINER_TRACK_T *track; + VC_CONTAINER_TRACK_MODULE_T *t_module; + VC_CONTAINER_STATUS_T status; + + if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && p_packet->track) + return VC_CONTAINER_ERROR_INVALID_ARGUMENT; + + track = p_ctx->tracks[0]; + t_module = track->priv->module; + + CLEAR_BIT(t_module->flags, TRACK_NEW_PACKET); + + while (!BITS_AVAILABLE(p_ctx, &t_module->payload)) + { + uint32_t bytes_read; + + /* No data left from last RTP packet, get another one */ + bytes_read = READ_BYTES(p_ctx, t_module->buffer, MAXIMUM_PACKET_SIZE); + if (!bytes_read) + return STREAM_STATUS(p_ctx); + + BITS_INIT(p_ctx, &t_module->payload, t_module->buffer, bytes_read); + + decode_rtp_packet_header(p_ctx, t_module); + SET_BIT(t_module->flags, TRACK_NEW_PACKET); + } + + if (p_packet) + { + uint32_t timestamp_top = t_module->timestamp >> 30; + + /* Determine whether timestamp has wrapped forwards or backwards around zero */ + if ((timestamp_top == 0) && (t_module->last_timestamp_top == 3)) + t_module->timestamp_wraps++; + else if ((timestamp_top == 3) && (t_module->last_timestamp_top == 0)) + t_module->timestamp_wraps--; + t_module->last_timestamp_top = timestamp_top; + + p_packet->dts = p_packet->pts = ((int64_t)t_module->timestamp_wraps << 32) | t_module->timestamp; + p_packet->track = 0; + p_packet->flags = 0; + } + + status = t_module->payload_handler(p_ctx, track, p_packet, flags); + if (p_packet && status == VC_CONTAINER_SUCCESS) + { + /* Adjust timestamps from RTP clock rate to microseconds */ + p_packet->pts = p_packet->pts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock; + p_packet->dts = p_packet->dts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock; + } + + STREAM_STATUS(p_ctx) = status; + return status; +} + +/**************************************************************************//** + * Seek over data in the container. + * + * @param p_ctx The reader context. + * @param p_offset The seek offset. + * @param mode The seek mode. + * @param flags The seek flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtp_reader_seek( VC_CONTAINER_T *p_ctx, + int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(p_offset); + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + + /* RTP is a non-seekable container */ + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; +} + +/**************************************************************************//** + * Apply a control operation to the container. + * + * @param p_ctx The reader context. + * @param operation The control operation. + * @param args Optional additional arguments for the operation. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtp_reader_control( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_CONTROL_T operation, + va_list args) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[0]->priv->module; + + switch (operation) + { + case VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE: + { + t_module->timestamp_base = va_arg(args, uint32_t); + if (!t_module->timestamp_base) + t_module->timestamp_base = 1; /* Zero is used to mean "not set" */ + status = VC_CONTAINER_SUCCESS; + } + break; + case VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER: + { + init_sequence_number(t_module, (uint16_t)va_arg(args, uint32_t)); + t_module->probation = 0; + status = VC_CONTAINER_SUCCESS; + } + break; + case VC_CONTAINER_CONTROL_SET_SOURCE_ID: + { + t_module->expected_ssrc = va_arg(args, uint32_t); + SET_BIT(t_module->flags, TRACK_SSRC_SET); + status = VC_CONTAINER_SUCCESS; + } + break; + default: + status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } + + return status; +} + +/**************************************************************************//** + * Close the container. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtp_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + vc_container_assert(p_ctx->tracks_num < 2); + + if (p_ctx->tracks_num) + { + void *payload_extra; + + vc_container_assert(module); + vc_container_assert(module->track); + vc_container_assert(module->track->priv); + vc_container_assert(module->track->priv->module); + + payload_extra = module->track->priv->module->extra; + if (payload_extra) + free(payload_extra); + vc_container_free_track(p_ctx, module->track); + } + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + if (module) free(module); + p_ctx->priv->module = 0; + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Open the container. + * Uses the I/O URI and/or data to configure the container. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_TRACK_T *track = 0; + VC_CONTAINER_TRACK_MODULE_T *t_module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINERS_LIST_T *parameters = NULL; + uint32_t payload_type; + uint32_t initial_seq_num; + + /* Check the URI scheme looks valid */ + if (!vc_uri_scheme(p_ctx->priv->uri) || + (strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTP_SCHEME) && + strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTP_PKT_SCHEME))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Make the query/parameter list more easily searchable */ + parameters = fill_parameter_list(p_ctx->priv->uri); + if (!parameters) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + /* Payload type parameter is mandatory and must fit in 7 bits */ + if (!rtp_get_parameter_u32(parameters, PAYLOAD_TYPE_NAME, &payload_type) || payload_type > 127) + { + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto error; + } + + /* Allocate our context */ + module = (VC_CONTAINER_MODULE_T *)malloc(sizeof(VC_CONTAINER_MODULE_T)); + if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = &module->track; + + /* Allocate the track, including space for reading an RTP packet on the end */ + track = vc_container_allocate_track(p_ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T) + MAXIMUM_PACKET_SIZE); + if (!track) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + module->track = track; + p_ctx->tracks_num = 1; + t_module = track->priv->module; + + /* Initialise the track data */ + t_module->buffer = (uint8_t *)(t_module + 1); + status = decode_payload_type(p_ctx, track, parameters, payload_type); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + vc_container_assert(t_module->timestamp_clock != 0); + + /* Default to a generic, unstructured payload handler */ + if (!t_module->payload_handler) + t_module->payload_handler = generic_payload_handler; + + if (rtp_get_parameter_x32(parameters, SSRC_NAME, &t_module->expected_ssrc)) + SET_BIT(t_module->flags, TRACK_SSRC_SET); + + t_module->probation = MIN_SEQUENTIAL; + if (rtp_get_parameter_u32(parameters, SEQ_NAME, &initial_seq_num)) + { + /* If an initial sequence number is provided, avoid probation period */ + t_module->max_seq_num = (uint16_t)initial_seq_num; + t_module->probation = 0; + } + + track->is_enabled = true; + + vc_containers_list_destroy(parameters); + + p_ctx->priv->pf_close = rtp_reader_close; + p_ctx->priv->pf_read = rtp_reader_read; + p_ctx->priv->pf_seek = rtp_reader_seek; + p_ctx->priv->pf_control = rtp_reader_control; + + return VC_CONTAINER_SUCCESS; + +error: + if (parameters) vc_containers_list_destroy(parameters); + if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS) + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + LOG_DEBUG(p_ctx, "error opening RTP (%i)", status); + rtp_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open rtp_reader_open +#endif diff --git a/containers/rtsp/CMakeLists.txt b/containers/rtsp/CMakeLists.txt new file mode 100755 index 0000000..5c386f6 --- /dev/null +++ b/containers/rtsp/CMakeLists.txt @@ -0,0 +1,14 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +set(rtsp_SRCS ${rtsp_SRCS} rtsp_reader.c) +add_library(reader_rtsp ${LIBRARY_TYPE} ${rtsp_SRCS}) + +target_link_libraries(reader_rtsp containers) + +install(TARGETS reader_rtsp DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/rtsp/rtsp_reader.c b/containers/rtsp/rtsp_reader.c new file mode 100755 index 0000000..2d96234 --- /dev/null +++ b/containers/rtsp/rtsp_reader.c @@ -0,0 +1,1983 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include + +#define CONTAINER_IS_BIG_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_list.h" +#include "containers/core/containers_uri.h" + +/****************************************************************************** +Configurable defines and constants. +******************************************************************************/ + +/** Maximum number of tracks allowed in an RTSP reader */ +#define RTSP_TRACKS_MAX 4 + +/** Space for sending requests and receiving responses */ +#define COMMS_BUFFER_SIZE 2048 + +/** Largest allowed RTSP URI. Must be substantially smaller than COMMS_BUFFER_SIZE + * to allow for the headers that may be sent. */ +#define RTSP_URI_LENGTH_MAX 1024 + +/** Maximum allowed length for the Session: header recevied in a SETUP response, + * This is to ensure comms buffer is not overflowed. */ +#define SESSION_HEADER_LENGTH_MAX 100 + +/** Number of milliseconds to block trying to read from the RTSP stream when no + * data is available from any of the tracks */ +#define DATA_UNAVAILABLE_READ_TIMEOUT_MS 1 + +/** Size of buffer for each track to use when receiving packets */ +#define UDP_READ_BUFFER_SIZE 520000 + +/* Arbitrary number of different dynamic ports to try */ +#define DYNAMIC_PORT_ATTEMPTS_MAX 16 + +/****************************************************************************** +Defines and constants. +******************************************************************************/ + +#define RTSP_SCHEME "rtsp:" +#define RTP_SCHEME "rtp" + +/** The RTSP PKT scheme is used with test pkt files */ +#define RTSP_PKT_SCHEME "rtsppkt:" + +#define RTSP_NETWORK_URI_START "rtsp://" +#define RTSP_NETWORK_URI_START_LENGTH (sizeof(RTSP_NETWORK_URI_START)-1) + +/** Initial capacity of header list */ +#define HEADER_LIST_INITIAL_CAPACITY 16 + +/** Format of the first line of an RTSP request */ +#define RTSP_REQUEST_LINE_FORMAT "%s %s RTSP/1.0\r\n" + +/** Format string for common headers used with all request methods. + * Note: includes double new line to terminate headers */ +#define TRAILING_HEADERS_FORMAT "CSeq: %u\r\nConnection: Keep-Alive\r\nUser-Agent: Broadcom/1.0\r\n\r\n" + +/** Format for the Transport: header */ +#define TRANSPORT_HEADER_FORMAT "Transport: RTP/AVP;unicast;client_port=%hu-%hu;mode=play\r\n" + +/** Format for including Session: header. */ +#define SESSION_HEADER_FORMAT "Session: %s\r\n" + +/** \name RTSP methods, used as the first item in the request line + * @{ */ +#define DESCRIBE_METHOD "DESCRIBE" +#define SETUP_METHOD "SETUP" +#define PLAY_METHOD "PLAY" +#define TEARDOWN_METHOD "TEARDOWN" +/* @} */ + +/** \name Names of headers used by the code + * @{ */ +#define CONTENT_PSEUDOHEADER_NAME ":" +#define CONTENT_LENGTH_NAME "Content-Length" +#define CONTENT_BASE_NAME "Content-Base" +#define CONTENT_LOCATION_NAME "Content-Location" +#define RTP_INFO_NAME "RTP-Info" +#define SESSION_NAME "Session" +/* @} */ + +/** Supported RTSP major version number */ +#define RTSP_MAJOR_VERSION 1 +/** Supported RTSP minor version number */ +#define RTSP_MINOR_VERSION 0 + +/** Lowest successful status code value */ +#define RTSP_STATUS_OK 200 +/** Next failure status code after the set of successful ones */ +#define RTSP_STATUS_MULTIPLE_CHOICES 300 + +/** Maximum size of a decimal string representation of a uint16_t, plus NUL */ +#define PORT_BUFFER_SIZE 6 +/** Start of private / dynamic port region */ +#define FIRST_DYNAMIC_PORT 0xC000 +/** End of private / dynamic port region */ +#define LAST_DYNAMIC_PORT 0xFFF0 + +/** Format of RTP track file extension */ +#define RTP_PATH_EXTENSION_FORMAT ".t%u.pkt" +/** Extra space need for creating an RTP track file name from an RTSP URI path */ +#define RTP_PATH_EXTRA 17 + +/** \name RTP URI parameter names + * @{ */ +#define PAYLOAD_TYPE_NAME "rtppt" +#define MIME_TYPE_NAME "mime-type" +#define SAMPLE_RATE_NAME "rate" +#define CHANNELS_NAME "channels" +/* @} */ + +/** Largest signed 64-bit integer */ +#define MAXIMUM_INT64 (int64_t)((1ULL << 63) - 1) + +/****************************************************************************** +Type definitions +******************************************************************************/ + +typedef int (*PARSE_IS_DELIMITER_FN_T)(int char_to_test); + +typedef struct rtsp_header_tag +{ + const char *name; + char *value; +} RTSP_HEADER_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + VC_CONTAINER_T *reader; /**< RTP reader for track */ + VC_URI_PARTS_T *reader_uri; /**< URI built up from SDP and used to open reader */ + char *control_uri; /**< URI used to control track playback */ + char *session_header; /**< Session header to be used when sending control requests */ + char *payload_type; /**< RTP payload type for track */ + char *media_type; /**< MIME type for track */ + VC_CONTAINER_PACKET_T info; /**< Latest track packet info block */ + unsigned short rtp_port; /**< UDP listener port being used in RTP reader */ +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *tracks[RTSP_TRACKS_MAX]; + char *comms_buffer; /**< Buffer used for sending and receiving RTSP messages */ + VC_CONTAINERS_LIST_T *header_list; /**< Parsed response headers, pointing into comms buffer */ + uint32_t cseq_value; /**< CSeq header value for next request */ + uint16_t next_rtp_port; /**< Next RTP port to use when opening track reader */ + uint16_t media_item; /**< Current media item number during initialization */ + bool uri_has_network_info; /**< True if the RTSP URI contains network info */ + int64_t ts_base; /**< Base value for dts and pts */ + VC_CONTAINER_TRACK_MODULE_T *current_track; /**< Next track to be read, to keep info/data on same track */ +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +static int rtsp_header_comparator(const RTSP_HEADER_T *first, const RTSP_HEADER_T *second); + +VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/**************************************************************************//** + * Trim whitespace from the end and start of the string + * + * \param str String to be trimmed + * \return Trimmed string + */ +static char *rtsp_trim( char *str ) +{ + char *trim = str + strlen(str); + + /* Search backwards for first non-whitespace */ + while (--trim >= str && isspace((int)*trim)) + ; /* Everything done in the while */ + trim[1] = '\0'; + + /* Now move start of string forwards to first non-whitespace */ + trim = str; + while (isspace((int)*trim)) + trim++; + + return trim; +} + +/**************************************************************************//** + * Send out the data in the comms buffer. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_send( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t to_write; + uint32_t written; + const char *buffer = module->comms_buffer; + + /* When reading from a captured file, do not attempt to send data */ + if (!module->uri_has_network_info) + return VC_CONTAINER_SUCCESS; + + to_write = strlen(buffer); + + while (to_write) + { + written = vc_container_io_write(p_ctx->priv->io, buffer, to_write); + if (!written) + break; + to_write -= written; + buffer += written; + } + + return p_ctx->priv->io->status; +} + +/**************************************************************************//** + * Send a DESCRIBE request to the RTSP server. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_send_describe_request( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; + char *uri = p_ctx->priv->io->uri; + + if (strlen(uri) > RTSP_URI_LENGTH_MAX) + { + LOG_ERROR(p_ctx, "RTSP: URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); + return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + } + + ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, DESCRIBE_METHOD, uri); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); + vc_container_assert(ptr < end); + + return rtsp_send(p_ctx); +} + +/**************************************************************************//** + * Send a SETUP request to the RTSP server. + * + * @param p_ctx The reader context. + * @param t_module The track module relating to the SETUP. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_send_setup_request( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; + char *uri = t_module->control_uri; + + if (strlen(uri) > RTSP_URI_LENGTH_MAX) + { + LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); + return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + } + + ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, SETUP_METHOD, uri); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRANSPORT_HEADER_FORMAT, t_module->rtp_port, t_module->rtp_port + 1); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); + vc_container_assert(ptr < end); + + return rtsp_send(p_ctx); +} + +/**************************************************************************//** + * Send a PLAY request to the RTSP server. + * + * @param p_ctx The reader context. + * @param t_module The track module relating to the PLAY. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_send_play_request( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; + char *uri = t_module->control_uri; + + if (strlen(uri) > RTSP_URI_LENGTH_MAX) + { + LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); + return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + } + + ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, PLAY_METHOD, uri); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, SESSION_HEADER_FORMAT, t_module->session_header); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); + vc_container_assert(ptr < end); + + return rtsp_send(p_ctx); +} + +/**************************************************************************//** + * Send a TEARDOWN request to the RTSP server. + * + * @param p_ctx The reader context. + * @param t_module The track module relating to the SETUP. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_send_teardown_request( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE; + char *uri = t_module->control_uri; + + if (strlen(uri) > RTSP_URI_LENGTH_MAX) + { + LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX); + return VC_CONTAINER_ERROR_URI_OPEN_FAILED; + } + + ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, TEARDOWN_METHOD, uri); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, SESSION_HEADER_FORMAT, t_module->session_header); + if (ptr < end) + ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++); + vc_container_assert(ptr < end); + + return rtsp_send(p_ctx); +} + +/**************************************************************************//** + * Check a response status line to see if the response is usable or not. + * Reasons for invalidity include: + * - Incorrectly formatted + * - Unsupported version + * - Status code is not in the 2xx range + * + * @param p_ctx The reader context. + * @param status_line The response status line. + * @return The resulting status of the function. + */ +static bool rtsp_successful_response_status( VC_CONTAINER_T *p_ctx, + const char *status_line) +{ + unsigned int major_version, minor_version, status_code; + + /* coverity[secure_coding] String is null-terminated */ + if (sscanf(status_line, "RTSP/%u.%u %u", &major_version, &minor_version, &status_code) != 3) + { + LOG_ERROR(p_ctx, "RTSP: Invalid response status line:\n%s", status_line); + return false; + } + + if (major_version != RTSP_MAJOR_VERSION || minor_version != RTSP_MINOR_VERSION) + { + LOG_ERROR(p_ctx, "RTSP: Unexpected response RTSP version: %u.%u", major_version, minor_version); + return false; + } + + if (status_code < RTSP_STATUS_OK || status_code >= RTSP_STATUS_MULTIPLE_CHOICES) + { + LOG_ERROR(p_ctx, "RTSP: Response status unsuccessful:\n%s", status_line); + return false; + } + + return true; +} + +/**************************************************************************//** + * Get the content length header from the response headers as an unsigned + * 32-bit integer. + * If the content length header is not found or badly formatted, zero is + * returned. + * + * @param header_list The response headers. + * @return The content length. + */ +static uint32_t rtsp_get_content_length( VC_CONTAINERS_LIST_T *header_list ) +{ + unsigned int content_length = 0; + RTSP_HEADER_T header; + + header.name = CONTENT_LENGTH_NAME; + if (header_list && vc_containers_list_find_entry(header_list, &header)) + /* coverity[secure_coding] String is null-terminated */ + sscanf(header.value, "%u", &content_length); + + return content_length; +} + +/**************************************************************************//** + * Get the session header from the response headers. + * If the session header is not found, the empty string is returned. + * + * @param header_list The response headers. + * @return The session header. + */ +static const char *rtsp_get_session_header(VC_CONTAINERS_LIST_T *header_list) +{ + RTSP_HEADER_T header; + + header.name = SESSION_NAME; + if (header_list && vc_containers_list_find_entry(header_list, &header)) + return header.value; + + return ""; +} + +/**************************************************************************//** + * Returns pointer to the string with any leading whitespace trimmed and + * terminated by a delimiter, as determined by is_delimiter_fn. The delimiter + * character replaced by NUL (to terminate the string) is returned in the + * variable pointed at by p_delimiter_replaced, if it is not NULL. + * + * The parse_str pointer is moved on to the character after the delimiter, or + * the end of the string if it is reached first. + * + * @param parse_str Pointer to the string pointer to parse. + * @param is_delimiter_fn Function to test if a character is a delimiter or not. + * @param p_delimiter_replaced Pointer to variable to receive delimiter character, or NULL. + * @return Pointer to extracted string. + */ +static char *rtsp_parse_extract(char **parse_str, + PARSE_IS_DELIMITER_FN_T is_delimiter_fn, + char *p_delimiter_replaced) +{ + char *ptr; + char *result; + + vc_container_assert(parse_str); + vc_container_assert(*parse_str); + vc_container_assert(is_delimiter_fn); + + ptr = *parse_str; + + while (isspace((int)*ptr)) + ptr++; + + result = ptr; + + while (*ptr && !(*is_delimiter_fn)(*ptr)) + ptr++; + if (p_delimiter_replaced) + *p_delimiter_replaced = *ptr; + if (*ptr) + *ptr++ = '\0'; + + *parse_str = ptr; + return result; +} + +/**************************************************************************//** + * Specialised form of rtsp_parse_extract() where the delimiter is whitespace. + * Returns pointer to the string with any leading whitespace trimmed and + * terminated by further whitespace. + * + * The parse_str pointer is moved on to the character after the delimiter, or + * the end of the string if it is reached first. + * + * @param parse_str Pointer to the string pointer to parse. + * @return Pointer to extracted string. + */ +static char *rtsp_parse_extract_ws(char **parse_str) +{ + char *ptr; + char *result; + + vc_container_assert(parse_str); + vc_container_assert(*parse_str); + + ptr = *parse_str; + + while (isspace((int)*ptr)) + ptr++; + + result = ptr; + + while (*ptr && !isspace((int)*ptr)) + ptr++; + if (*ptr) + *ptr++ = '\0'; + + *parse_str = ptr; + return result; +} + +/**************************************************************************//** + * Returns whether the given character is a parameter name delimiter or not. + * + * @param char_to_test The character under test. + * @return True if the character is a name delimiter, false if not. + */ +static int name_delimiter_fn(int char_to_test) +{ + switch (char_to_test) + { + case ' ': + case '\t': + case '=': + case ';': + return true; + default: + return false; + } +} + +/**************************************************************************//** + * Returns whether the given character is a parameter value delimiter or not. + * + * @param char_to_test The character under test. + * @return True if the character is a value delimiter, false if not. + */ +static int value_delimiter_fn(int char_to_test) +{ + switch (char_to_test) + { + case ' ': + case '\t': + case ';': + return true; + default: + return false; + } +} + +/**************************************************************************//** + * Extract a name/value pair from a given string. + * Each pair consists of a name, optionally followed by '=' and a value, with + * optional whitespace around either or both name and value. The parameter is + * terminated by a semi-colon, ';'. + * + * The parse_str pointer is moved on to the next parameter, or the end of the + * string if that is reached first. + * + * Name can be empty if there are two consecutive semi-colons, or a trailing + * semi-colon. + * + * @param parse_str Pointer to the string pointer to be parsed. + * @param p_name Pointer to where name string pointer shall be written. + * @param p_value Pointer to where value string pointer shall be written. + * @return True if the name is not empty. + */ +static bool rtsp_parse_extract_parameter(char **parse_str, char **p_name, char **p_value) +{ + char delimiter; + + vc_container_assert(parse_str); + vc_container_assert(*parse_str); + vc_container_assert(p_name); + vc_container_assert(p_value); + + /* General form of each parameter: + * [=] + * but allow for spaces before and after name and value */ + *p_name = rtsp_parse_extract(parse_str, name_delimiter_fn, &delimiter); + if (isspace((int)delimiter)) + { + /* Skip further spaces after parameter name */ + do { + delimiter = **parse_str; + if (delimiter) + (*parse_str)++; + } while (isspace((int)delimiter)); + } + + if (delimiter == '=') + { + /* Parameter value present (although may be empty) */ + *p_value = rtsp_parse_extract(parse_str, value_delimiter_fn, &delimiter); + if (isspace((int)delimiter)) + { + /* Skip spaces after parameter value */ + do { + delimiter = **parse_str; + if (delimiter) + (*parse_str)++; + } while (isspace((int)delimiter)); + } + } else { + *p_value = NULL; + } + + return (**p_name != '\0'); +} + +/**************************************************************************//** + * Parses RTP-Info header and stores relevant parts. + * + * @param header_list The response header list. + * @param t_module The track module relating to the response headers. + */ +static void rtsp_store_rtp_info(VC_CONTAINERS_LIST_T *header_list, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + RTSP_HEADER_T header; + char *ptr; + + header.name = RTP_INFO_NAME; + if (!vc_containers_list_find_entry(header_list, &header)) + return; + + ptr = header.value; + while (ptr && *ptr) + { + char *name; + char *value; + + if (!rtsp_parse_extract_parameter(&ptr, &name, &value)) + continue; + + if (strcasecmp(name, "rtptime") == 0) + { + unsigned int timestamp_base = 0; + + /* coverity[secure_coding] String is null-terminated */ + if (sscanf(value, "%u", ×tamp_base) == 1) + (void)vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE, timestamp_base); + } + else if (strcasecmp(name, "seq") == 0) + { + unsigned short int sequence_number = 0; + + /* coverity[secure_coding] String is null-terminated */ + if (sscanf(value, "%hu", &sequence_number) == 1) + (void)vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER, (uint32_t)sequence_number); + } + } +} + +/**************************************************************************//** + * Reads an RTSP response and parses it into headers and content. + * The headers and content remain stored in the comms buffer, but referenced + * by the module's header list. Content uses a special header name that cannot + * occur in the real headers. + * + * @param p_ctx The RTSP reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_read_response( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_IO_T *p_ctx_io = p_ctx->priv->io; + char *next_read = module->comms_buffer; + uint32_t space_available = COMMS_BUFFER_SIZE - 1; /* Allow for a NUL */ + uint32_t received; + char *ptr = next_read; + bool found_content = false; + RTSP_HEADER_T header; + + vc_containers_list_reset(module->header_list); + + /* Response status line doesn't need to be stored, just checked */ + header.name = NULL; + header.value = next_read; + + while (space_available) + { + received = vc_container_io_read(p_ctx_io, next_read, space_available); + if (p_ctx_io->status != VC_CONTAINER_SUCCESS) + break; + + next_read += received; + space_available -= received; + + while (!found_content && ptr < next_read) + { + switch (*ptr) + { + case ':': + if (header.value) + { + /* Just another character in the value */ + ptr++; + } else { + /* End of name, expect value next */ + *ptr++ = '\0'; + header.value = ptr; + } + break; + + case '\n': + if (header.value) + { + /* End of line while parsing the value part of the header, add name/value pair to list */ + *ptr++ = '\0'; + header.value = rtsp_trim(header.value); + if (header.name) + { + if (!vc_containers_list_insert(module->header_list, &header, false)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to add <%s> header to list", header.name); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + } else { + /* Check response status line */ + if (!rtsp_successful_response_status(p_ctx, header.value)) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + /* Ready for next header */ + header.name = ptr; + header.value = NULL; + } else { + uint32_t content_length; + + /* End of line while parsing the name of a header */ + *ptr++ = '\0'; + if (*header.name && *header.name != '\r') + { + /* A non-empty name is invalid, so fail */ + LOG_ERROR(p_ctx, "RTSP: Invalid name in header - no colon:\n%s", header.name); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* An empty name signifies the start of the content has been found */ + found_content = true; + + /* Make a pseudo-header for the content and add it to the list */ + header.name = CONTENT_PSEUDOHEADER_NAME; + header.value = ptr; + if (!vc_containers_list_insert(module->header_list, &header, false)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to add content pseudoheader to list"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + + /* Calculate how much content there is left to read, based on Content-Length header */ + content_length = rtsp_get_content_length(module->header_list); + if (ptr + content_length < next_read) + { + /* Final content byte already present, with extra data after it */ + space_available = 0; + } else { + uint32_t content_to_read = content_length - (next_read - ptr); + + if (content_to_read >= space_available) + { + LOG_ERROR(p_ctx, "RTSP: Not enough room to read content"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + + /* Restrict further reading to the number of content bytes left */ + space_available = content_to_read; + } + } + break; + + default: + /* Just another character in either the name or the value */ + ptr++; + } + } + } + + if (!space_available) + { + if (found_content) + { + /* Terminate content region */ + *next_read = '\0'; + } else { + /* Ran out of buffer space and never found the content */ + LOG_ERROR(p_ctx, "RTSP: Response header section too big / content missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + } + + return p_ctx_io->status; +} + +/**************************************************************************//** + * Creates a new track from an SDP media field. + * Limitation: only the first payload type of the field is used. + * + * @param p_ctx The RTSP reader context. + * @param media The media field. + * @param p_track Pointer to the variable to receive the new track pointer. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_create_track_for_media_field(VC_CONTAINER_T *p_ctx, + char *media, + VC_CONTAINER_TRACK_T **p_track ) +{ + VC_CONTAINER_TRACK_T *track = NULL; + VC_CONTAINER_TRACK_MODULE_T *t_module = NULL; + char *ptr = media; + char *media_type; + char *rtp_port; + char *transport_type; + char *payload_type; + + *p_track = NULL; + if (p_ctx->tracks_num == RTSP_TRACKS_MAX) + { + LOG_DEBUG(p_ctx, "RTSP: Too many media items in SDP data, only %d are supported.", RTSP_TRACKS_MAX); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + /* Format of media item: + * m= + * Only RTP/AVP transport and the first payload type are supported */ + media_type = rtsp_parse_extract_ws(&ptr); + rtp_port = rtsp_parse_extract_ws(&ptr); + transport_type = rtsp_parse_extract_ws(&ptr); + payload_type = rtsp_parse_extract_ws(&ptr); + if (!*media_type || !*rtp_port || strcmp(transport_type, "RTP/AVP") || !*payload_type) + { + LOG_ERROR(p_ctx, "RTSP: Failure to parse media field"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + track = vc_container_allocate_track(p_ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T)); + if (!track) goto out_of_memory_error; + t_module = track->priv->module; + + /* If the port specifier is invalid, treat it as if it were zero */ + /* coverity[secure_coding] String is null-terminated */ + sscanf(rtp_port, "%hu", &t_module->rtp_port); + t_module->payload_type = payload_type; + t_module->media_type = media_type; + + t_module->reader_uri = vc_uri_create(); + if (!t_module->reader_uri) goto out_of_memory_error; + if (!vc_uri_set_scheme(t_module->reader_uri, RTP_SCHEME)) goto out_of_memory_error; + if (!vc_uri_add_query(t_module->reader_uri, PAYLOAD_TYPE_NAME, payload_type)) goto out_of_memory_error; + + p_ctx->tracks[p_ctx->tracks_num++] = track; + *p_track = track; + return VC_CONTAINER_SUCCESS; + +out_of_memory_error: + if (track) + { + if (t_module->reader_uri) + vc_uri_release(t_module->reader_uri); + vc_container_free_track(p_ctx, track); + } + LOG_ERROR(p_ctx, "RTSP: Memory allocation failure creating track"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; +} + +/**************************************************************************//** + * Returns whether the given character is a slash or not. + * + * @param char_to_test The character under test. + * @return True if the character is a slash, false if not. + */ +static int slash_delimiter_fn(int char_to_test) +{ + return char_to_test == '/'; +} + +/**************************************************************************//** + * Parse an rtpmap attribute and store values in the related track. + * + * @param p_ctx The RTSP reader context. + * @param track The track relating to the rtpmap. + * @param attribute The rtpmap attribute value. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_parse_rtpmap_attribute( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + char *attribute ) +{ + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + char *ptr = attribute; + char *payload_type; + char *mime_sub_type; + char *sample_rate; + char *full_mime_type; + char *channels; + + /* rtpmap attribute format: + * /[/] + * Payload type must match the one used in the media field */ + payload_type = rtsp_parse_extract_ws(&ptr); + if (strcmp(payload_type, t_module->payload_type)) + { + /* Ignore any unsupported secondary payload type attributes */ + LOG_DEBUG(p_ctx, "RTSP: Secondary payload type attribute - not supported"); + return VC_CONTAINER_SUCCESS; + } + + mime_sub_type = rtsp_parse_extract(&ptr, slash_delimiter_fn, NULL); + if (!*mime_sub_type) + { + LOG_ERROR(p_ctx, "RTSP: rtpmap: MIME type missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + sample_rate = rtsp_parse_extract(&ptr, slash_delimiter_fn, NULL); + if (!*sample_rate) + { + LOG_ERROR(p_ctx, "RTSP: rtpmap: sample rate missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + full_mime_type = (char *)malloc(strlen(t_module->media_type) + strlen(mime_sub_type) + 2); + if (!full_mime_type) + { + LOG_ERROR(p_ctx, "RTSP: Failed to allocate space for full MIME type"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + /* coverity[secure_coding] String has been allocated of the right size */ + sprintf(full_mime_type, "%s/%s", t_module->media_type, mime_sub_type); + if (!vc_uri_add_query(t_module->reader_uri, MIME_TYPE_NAME, full_mime_type)) + { + free(full_mime_type); + LOG_ERROR(p_ctx, "RTSP: Failed to add MIME type to URI"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + free(full_mime_type); + + if (!vc_uri_add_query(t_module->reader_uri, SAMPLE_RATE_NAME, sample_rate)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to add sample rate to URI"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + + /* Optional channels specifier */ + channels = rtsp_parse_extract_ws(&ptr); + if (*channels) + { + if (!vc_uri_add_query(t_module->reader_uri, CHANNELS_NAME, channels)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to add channels to URI"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Parse an fmtp attribute and store values in the related track. + * + * @param p_ctx The RTSP reader context. + * @param track The track relating to the fmtp. + * @param attribute The fmtp attribute value. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_parse_fmtp_attribute( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track, + char *attribute ) +{ + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + char *ptr = attribute; + char *payload_type; + + /* fmtp attribute format: + * + * The payload type must match the first one in the media field, parameters + * are semi-colon separated and may have additional whitespace around them. */ + + payload_type = rtsp_parse_extract_ws(&ptr); + if (strcmp(payload_type, t_module->payload_type)) + { + /* Ignore any unsupported secondary payload type attributes */ + LOG_DEBUG(p_ctx, "RTSP: Secondary payload type attribute - not supported"); + return VC_CONTAINER_SUCCESS; + } + + while (*ptr) + { + char *name; + char *value; + + /* Only add the parameter if the name was not empty. This avoids problems with + * strings like ";;", ";" or ";=value;" */ + if (rtsp_parse_extract_parameter(&ptr, &name, &value)) + { + if (!vc_uri_add_query(t_module->reader_uri, name, value)) + { + if (value) + LOG_ERROR(p_ctx, "RTSP: Failed to add <%s>=<%s> query to URI", name, value); + else + LOG_ERROR(p_ctx, "RTSP: Failed to add <%s> query to URI", name); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + } + } + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Merge base URI and relative URI strings into a merged URI string. + * Always creates a new string, even if the relative URI is actually absolute. + * + * @param p_ctx The RTSP reader context. + * @param base_uri_str The base URI string. + * @param relative_uri_str The relative URI string. + * @param p_merged_uri_str Pointer to where to put the merged string pointer. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_merge_uris( VC_CONTAINER_T *p_ctx, + const char *base_uri_str, + const char *relative_uri_str, + char **p_merged_uri_str) +{ + VC_URI_PARTS_T *base_uri = NULL; + VC_URI_PARTS_T *relative_uri = NULL; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + uint32_t merged_size; + + *p_merged_uri_str = NULL; + relative_uri = vc_uri_create(); + if (!relative_uri) goto tidy_up; + if (!vc_uri_parse(relative_uri, relative_uri_str)) + { + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto tidy_up; + } + + if (vc_uri_scheme(relative_uri) != NULL) + { + /* URI is absolute, not relative, so return it as the merged URI */ + size_t len = strlen(relative_uri_str); + + *p_merged_uri_str = (char *)malloc(len + 1); + if (!*p_merged_uri_str) goto tidy_up; + + strncpy(*p_merged_uri_str, relative_uri_str, len); + status = VC_CONTAINER_SUCCESS; + goto tidy_up; + } + + base_uri = vc_uri_create(); + if (!base_uri) goto tidy_up; + if (!vc_uri_parse(base_uri, base_uri_str)) + { + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto tidy_up; + } + + /* Build up merged URI in relative_uri, using base_uri as necessary */ + if (!vc_uri_merge(base_uri, relative_uri)) goto tidy_up; + + merged_size = vc_uri_build(relative_uri, NULL, 0) + 1; + *p_merged_uri_str = (char *)malloc(merged_size); + if (!*p_merged_uri_str) goto tidy_up; + + vc_uri_build(relative_uri, *p_merged_uri_str, merged_size); + + status = VC_CONTAINER_SUCCESS; + +tidy_up: + if (base_uri) vc_uri_release(base_uri); + if (relative_uri) vc_uri_release(relative_uri); + if (status != VC_CONTAINER_SUCCESS) + LOG_ERROR(p_ctx, "RTSP: Error merging URIs: %d", (int)status); + return status; +} + +/**************************************************************************//** + * Parse a control attribute and store it as an absolute URI. + * + * @param p_ctx The RTSP reader context. + * @param attribute The control attribute value. + * @param base_uri_str The base URI string. + * @param p_control_uri_str Pointer to where to put the control string pointer. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_parse_control_attribute( VC_CONTAINER_T *p_ctx, + const char *attribute, + const char *base_uri_str, + char **p_control_uri_str) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + /* control attribute format: + * + * The control URI is either absolute or relative to the base URI. If the + * control URI is just an asterisk, the control URI matches the base URI. */ + + if (!*attribute || strcmp(attribute, "*") == 0) + { + size_t len = strlen(base_uri_str); + + *p_control_uri_str = (char *)malloc(len + 1); + if (!*p_control_uri_str) + { + LOG_ERROR(p_ctx, "RTSP: Failed to allocate control URI"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + strncpy(*p_control_uri_str, base_uri_str, len); + } else { + status = rtsp_merge_uris(p_ctx, base_uri_str, attribute, p_control_uri_str); + } + + return status; +} + +/**************************************************************************//** + * Open a reader for the track using the URI that has been generated. + * + * @param p_ctx The RTSP reader context. + * @param t_module The track module for which a reader is needed. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_open_track_reader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t uri_buffer_size; + char *uri_buffer; + + uri_buffer_size = vc_uri_build(t_module->reader_uri, NULL, 0) + 1; + uri_buffer = (char *)malloc(uri_buffer_size); + if (!uri_buffer) + { + LOG_ERROR(p_ctx, "RTSP: Failed to build RTP URI"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + vc_uri_build(t_module->reader_uri, uri_buffer, uri_buffer_size); + + t_module->reader = vc_container_open_reader(uri_buffer, &status, NULL, NULL); + free(uri_buffer); + + return status; +} + +/**************************************************************************//** + * Open a reader for the track using the network URI that has been generated. + * + * @param p_ctx The RTSP reader context. + * @param t_module The track module for which a reader is needed. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_open_network_reader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + char port[PORT_BUFFER_SIZE] = {0}; + + if (!t_module->rtp_port) + { + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + t_module->rtp_port = module->next_rtp_port; + if (t_module->rtp_port > LAST_DYNAMIC_PORT) + { + LOG_ERROR(p_ctx, "RTSP: Out of dynamic ports"); + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + } + + module->next_rtp_port += 2; + } + + snprintf(port, sizeof(port)-1, "%hu", t_module->rtp_port); + if (!vc_uri_set_port(t_module->reader_uri, port)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to set track reader URI port"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + + return rtsp_open_track_reader(p_ctx, t_module); +} + +/**************************************************************************//** + * Open a reader for the track using the file URI that has been generated. + * + * @param p_ctx The RTSP reader context. + * @param t_module The track module for which a reader is needed. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_open_file_reader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_STATUS_T status; + VC_URI_PARTS_T *rtsp_uri = NULL; + const char *rtsp_path; + int len; + char *new_path = NULL; + char *extension; + + /* Use the RTSP URI's path, with the extension changed to ".t.pkt" + * where is the zero-based media item number. */ + + rtsp_uri = vc_uri_create(); + if (!rtsp_uri) + { + LOG_ERROR(p_ctx, "RTSP: Failed to create RTSP URI"); + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto tidy_up; + } + + if (!vc_uri_parse(rtsp_uri, p_ctx->priv->io->uri)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to parse RTSP URI <%s>", p_ctx->priv->io->uri); + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto tidy_up; + } + + rtsp_path = vc_uri_path(rtsp_uri); + if (!rtsp_path || !*rtsp_path) + { + LOG_ERROR(p_ctx, "RTSP: RTSP URI path missing <%s>", p_ctx->priv->io->uri); + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + goto tidy_up; + } + + len = strlen(rtsp_path); + new_path = (char *)calloc(1, len + RTP_PATH_EXTRA + 1); + if (!rtsp_uri) + { + LOG_ERROR(p_ctx, "RTSP: Failed to create buffer for RTP path"); + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto tidy_up; + } + + strncpy(new_path, rtsp_path, len); + extension = strrchr(new_path, '.'); /* Find extension, to replace it */ + if (!extension) + extension = new_path + strlen(new_path); /* No extension, so append instead */ + + snprintf(extension, len + RTP_PATH_EXTRA - (extension - new_path), + RTP_PATH_EXTENSION_FORMAT, p_ctx->priv->module->media_item); + if (!vc_uri_set_path(t_module->reader_uri, new_path)) + { + LOG_ERROR(p_ctx, "RTSP: Failed to store RTP path <%s>", new_path); + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto tidy_up; + } + + free(new_path); + vc_uri_release(rtsp_uri); + + return rtsp_open_track_reader(p_ctx, t_module); + +tidy_up: + if (new_path) free(new_path); + if (rtsp_uri) vc_uri_release(rtsp_uri); + return status; +} + +/**************************************************************************//** + * Copy track information from the encapsulated track reader's track to the + * RTSP track. + * + * @param p_ctx The RTSP reader context. + * @param track The RTSP track requiring its information to be filled in. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_copy_track_data_from_reader( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track ) +{ + VC_CONTAINER_T *reader = track->priv->module->reader; + VC_CONTAINER_ES_FORMAT_T *src_format, *dst_format; + + if (reader->tracks_num != 1) + { + LOG_ERROR(p_ctx, "RTSP: Expected track reader to have one track, has %d", reader->tracks_num); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + if (reader->tracks[0]->meta_num) + { + LOG_DEBUG(p_ctx, "RTSP: Track reader has meta data - not supported"); + } + + src_format = reader->tracks[0]->format; + dst_format = track->format; + + /* Copy fields individually to avoid problems with pointers (type and extradata). */ + dst_format->es_type = src_format->es_type; + dst_format->codec = src_format->codec; + dst_format->codec_variant = src_format->codec_variant; + *dst_format->type = *src_format->type; + dst_format->bitrate = src_format->bitrate; + memcpy(dst_format->language, src_format->language, sizeof(dst_format->language)); + dst_format->group_id = src_format->group_id; + dst_format->flags = src_format->flags; + + if (src_format->extradata) + { + VC_CONTAINER_STATUS_T status; + uint32_t extradata_size = src_format->extradata_size; + + status = vc_container_track_allocate_extradata(p_ctx, track, extradata_size); + if (status != VC_CONTAINER_SUCCESS) + return status; + + memcpy(dst_format->extradata, src_format->extradata, extradata_size); + dst_format->extradata_size = extradata_size; + } + + track->is_enabled = reader->tracks[0]->is_enabled; + + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Finalise the creation of the RTSP track by opening its reader and filling in + * the track information. + * + * @param p_ctx The RTSP reader context. + * @param track The RTSP track being finalised. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_complete_track( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + if (!t_module->control_uri) + { + LOG_ERROR(p_ctx, "RTSP: Track control URI is missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + + if (module->uri_has_network_info) + { + int ii; + + if (!vc_uri_set_host(t_module->reader_uri, "")) + { + LOG_ERROR(p_ctx, "RTSP: Failed to set track reader URI host"); + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + } + + status = rtsp_open_network_reader(p_ctx, t_module); + + for (ii = 0; status == VC_CONTAINER_ERROR_URI_OPEN_FAILED && ii < DYNAMIC_PORT_ATTEMPTS_MAX; ii++) + { + /* Reset port to pick up next dynamic port */ + t_module->rtp_port = 0; + status = rtsp_open_network_reader(p_ctx, t_module); + } + + /* Change I/O to non-blocking, so that tracks can be polled */ + if (status == VC_CONTAINER_SUCCESS) + status = vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, 0); + /* Set a large read buffer, to avoid dropping bursts of large packets (e.g. hi-def video) */ + if (status == VC_CONTAINER_SUCCESS) + status = vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE, UDP_READ_BUFFER_SIZE); + } else { + status = rtsp_open_file_reader(p_ctx, t_module); + } + + vc_uri_release(t_module->reader_uri); + t_module->reader_uri = NULL; + + if (status == VC_CONTAINER_SUCCESS) + status = rtsp_copy_track_data_from_reader(p_ctx, track); + + return status; +} + +/**************************************************************************//** + * Returns whether the given character is an attribute name delimiter or not. + * + * @param char_to_test The character under test. + * @return True if the character is an attribute name delimiter, false if not. + */ +static int attribute_name_delimiter_fn(int char_to_test) +{ + return (char_to_test == ':'); +} + +/**************************************************************************//** + * Create RTSP tracks from media fields in SDP formatted data. + * + * @param p_ctx The RTSP reader context. + * @param sdp_buffer The SDP data. + * @param base_uri The RTSP base URI. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_create_tracks_from_sdp( VC_CONTAINER_T *p_ctx, + char *sdp_buffer, + char *base_uri ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_T *track = NULL; + char *session_base_uri = base_uri; + char *ptr; + char *next_line_ptr; + char *attribute; + + for (ptr = sdp_buffer; status == VC_CONTAINER_SUCCESS && *ptr; ptr = next_line_ptr) + { + /* Find end of line */ + char field = *ptr; + + next_line_ptr = ptr; + while (*next_line_ptr && *next_line_ptr != '\n') + next_line_ptr++; + + /* Terminate line */ + if (*next_line_ptr) + *next_line_ptr++ = '\0'; + + /* The format of the line has to be "=" where is a single + * character. Ignore anything else. */ + if (ptr[1] != '=') + continue; + ptr = rtsp_trim(ptr + 2); + + switch (field) + { + case 'm': + /* Start of media item */ + if (track) + { + /* Finish previous track */ + status = rtsp_complete_track(p_ctx, track); + track = NULL; + p_ctx->priv->module->media_item++; + if (status != VC_CONTAINER_SUCCESS) + break; + } + + status = rtsp_create_track_for_media_field(p_ctx, ptr, &track); + break; + case 'a': /* Attribute (either session or media level) */ + /* Attributes of the form "a=:" */ + attribute = rtsp_parse_extract(&ptr, attribute_name_delimiter_fn, NULL); + + if (track) + { + /* Media level attribute */ + + /* Look for known attributes */ + if (strcmp(attribute, "rtpmap") == 0) + status = rtsp_parse_rtpmap_attribute(p_ctx, track, ptr); + else if (strcmp(attribute, "fmtp") == 0) + status = rtsp_parse_fmtp_attribute(p_ctx, track, ptr); + else if (strcmp(attribute, "control") == 0) + { + char **track_control_uri = &track->priv->module->control_uri; + + if (*track_control_uri) + { + free(*track_control_uri); + *track_control_uri = NULL; + } + status = rtsp_parse_control_attribute(p_ctx, ptr, session_base_uri, track_control_uri); + } + /* Any other attributes are ignored */ + } else { + /* Session level attribute */ + if (strcmp(attribute, "control") == 0) + { + /* Only need to change the session_base_uri if it differs from the base URI */ + ptr = rtsp_trim(ptr); + if (session_base_uri != base_uri) + { + free(session_base_uri); + session_base_uri = base_uri; + } + if (strcmp(ptr, base_uri) != 0) + status = rtsp_parse_control_attribute(p_ctx, ptr, base_uri, &session_base_uri); + } + } + break; + default: /* Ignore any other field names */ + ; + } + } + + if (session_base_uri && session_base_uri != base_uri) + free(session_base_uri); + + /* Having no media fields is an error, since there will be nothing to play back */ + if (status == VC_CONTAINER_SUCCESS) + { + if (!p_ctx->tracks_num) + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + else if (track) + { + /* Finish final track */ + status = rtsp_complete_track(p_ctx, track); + p_ctx->priv->module->media_item++; + } + } + + return status; +} + +/**************************************************************************//** + * Create RTSP tracks from the response to a DESCRIBE request. + * The response must have already been filled into the comms buffer and header + * list. + * + * @param p_ctx The RTSP reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_create_tracks_from_response( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINERS_LIST_T *header_list = p_ctx->priv->module->header_list; + RTSP_HEADER_T header; + char *base_uri; + char *content; + + header.name = CONTENT_PSEUDOHEADER_NAME; + if (!vc_containers_list_find_entry(header_list, &header)) + { + LOG_ERROR(p_ctx, "RTSP: Content missing"); + return VC_CONTAINER_ERROR_FORMAT_INVALID; + } + content = header.value; + + /* The control URI may be relative to a base URI which is the first of these + * that is available: + * 1. Content-Base header + * 2. Content-Location header + * 3. Request URI + */ + header.name = CONTENT_BASE_NAME; + if (vc_containers_list_find_entry(header_list, &header)) + base_uri = header.value; + else { + header.name = CONTENT_LOCATION_NAME; + if (vc_containers_list_find_entry(header_list, &header)) + base_uri = header.value; + else + base_uri = p_ctx->priv->io->uri; + } + + return rtsp_create_tracks_from_sdp(p_ctx, content, base_uri); +} + +/**************************************************************************//** + * Header comparison function. + * Compare two header structures and return whether the first is less than, + * equal to or greater than the second. + * + * @param first The first structure to be compared. + * @param second The second structure to be compared. + * @return Negative if first is less than second, positive if first is greater + * and zero if they are equal. + */ +static int rtsp_header_comparator(const RTSP_HEADER_T *first, const RTSP_HEADER_T *second) +{ + return strcasecmp(first->name, second->name); +} + +/**************************************************************************//** + * Make a DESCRIBE request to the server and create tracks from the response. + * + * @param p_ctx The RTSP reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_describe( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + + /* Send DESCRIBE request and get response */ + status = rtsp_send_describe_request(p_ctx); + if (status != VC_CONTAINER_SUCCESS) return status; + status = rtsp_read_response(p_ctx); + if (status != VC_CONTAINER_SUCCESS) return status; + + /* Create tracks from SDP content */ + status = rtsp_create_tracks_from_response(p_ctx); + + return status; +} + +/**************************************************************************//** + * Make a SETUP request to the server and get session from response. + * + * @param p_ctx The RTSP reader context. + * @param t_module The track module to be set up. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_setup( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + const char *session_header; + size_t session_header_len; + + status = rtsp_send_setup_request(p_ctx, t_module); + if (status != VC_CONTAINER_SUCCESS) return status; + status = rtsp_read_response(p_ctx); + if (status != VC_CONTAINER_SUCCESS) return status; + + session_header = rtsp_get_session_header(module->header_list); + session_header_len = strlen(session_header); + if (session_header_len > SESSION_HEADER_LENGTH_MAX) return VC_CONTAINER_ERROR_FORMAT_INVALID; + + t_module->session_header = (char *)malloc(session_header_len + 1); + if (!t_module->session_header) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + strncpy(t_module->session_header, session_header, session_header_len); + + return status; +} + +/**************************************************************************//** + * Make a SETUP request to the server and get session from response. + * + * @param p_ctx The RTSP reader context. + * @param t_module The track module to be set up. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_play( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_MODULE_T *t_module ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + + status = rtsp_send_play_request(p_ctx, t_module); + if (status != VC_CONTAINER_SUCCESS) return status; + status = rtsp_read_response(p_ctx); + if (status != VC_CONTAINER_SUCCESS) return status; + + rtsp_store_rtp_info(module->header_list, t_module); + + return status; +} + +/**************************************************************************//** + * Blocking read/skip data from a container. + * Can also be used to query information about the next block of data. + * + * @pre The container is set to non-blocking. + * @post The container is set to non-blocking. + * + * @param p_ctx The reader context. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_blocking_track_read(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags) +{ + VC_CONTAINER_STATUS_T status; + + status = vc_container_read(p_ctx, p_packet, flags); + + /* The ..._ABORTED status corresponds to a timeout waiting for data */ + if (status == VC_CONTAINER_ERROR_ABORTED) + { + /* So switch to blocking temporarily to wait for some */ + (void)vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, VC_CONTAINER_READ_TIMEOUT_BLOCK); + status = vc_container_read(p_ctx, p_packet, flags); + (void)vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, 0); + } + + return status; +} + +/**************************************************************************//** + * Update the cached packet info blocks for all tracks. + * If one or more of the tracks has data, set the current track to the one with + * the earliest decode timestamp. + * + * @pre The track readers must not block when data is requested from them. + * + * @param p_ctx The RTSP reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_update_track_info( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t tracks_num = p_ctx->tracks_num; + uint32_t track_idx; + int64_t earliest_dts = MAXIMUM_INT64; + VC_CONTAINER_TRACK_MODULE_T *earliest_track = NULL; + + /* Reset current track to unknown */ + p_ctx->priv->module->current_track = NULL; + + /* Collect each track's info and return the one with earliest timestamp. */ + for (track_idx = 0; track_idx < tracks_num; track_idx++) + { + VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[track_idx]->priv->module; + VC_CONTAINER_PACKET_T *info = &t_module->info; + + /* If this track has no data available, request more */ + if (!info->size) + { + /* This is a non-blocking read, so status will be ..._ABORTED if nothing available */ + status = vc_container_read(t_module->reader, info, VC_CONTAINER_READ_FLAG_INFO); + /* Adjust track index to be the RTSP index instead of the RTP one */ + info->track = track_idx; + } + + if (status == VC_CONTAINER_SUCCESS) + { + if (info->dts < earliest_dts) + { + earliest_dts = info->dts; + earliest_track = t_module; + } + } + else if (status != VC_CONTAINER_ERROR_ABORTED) + { + /* Not a time-out failure, so abort */ + return status; + } + } + + p_ctx->priv->module->current_track = earliest_track; + + return VC_CONTAINER_SUCCESS; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +/**************************************************************************//** + * Read/skip data from the container. + * Can also be used to query information about the next block of data. + * + * @param p_ctx The reader context. + * @param p_packet The container packet information, or NULL. + * @param flags The container read flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, + uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_TRACK_MODULE_T *current_track = module->current_track; + VC_CONTAINER_PACKET_T *info; + + if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + { + vc_container_assert(p_packet); + vc_container_assert(p_packet->track < p_ctx->tracks_num); + current_track = p_ctx->tracks[p_packet->track]->priv->module; + module->current_track = current_track; + + if (!current_track->info.size) + { + status = rtsp_blocking_track_read(current_track->reader, ¤t_track->info, VC_CONTAINER_READ_FLAG_INFO); + if (status != VC_CONTAINER_SUCCESS) + goto error; + } + } + else if (!current_track || !current_track->info.size) + { + status = rtsp_update_track_info(p_ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + while (!module->current_track) + { + /* Check RTSP stream to see if it has closed */ + status = rtsp_read_response(p_ctx); + if (status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_ABORTED) + { + /* No data from any track yet, so keep checking */ + status = rtsp_update_track_info(p_ctx); + } + if (status != VC_CONTAINER_SUCCESS) + goto error; + } + + current_track = module->current_track; + } + + info = ¤t_track->info; + vc_container_assert(info->size); + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + { + vc_container_assert(p_packet); + memcpy(p_packet, info, sizeof(*info)); + } else { + status = rtsp_blocking_track_read(current_track->reader, p_packet, flags); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + if (p_packet) + { + p_packet->track = info->track; + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + { + info->size = 0; + } else { + vc_container_assert(info->size >= p_packet->size); + info->size -= p_packet->size; + } + } else { + info->size = 0; + } + } + + if (p_packet) + { + /* Adjust timestamps to be relative to zero */ + if (!module->ts_base) + module->ts_base = p_packet->dts; + p_packet->dts -= module->ts_base; + p_packet->pts -= module->ts_base; + } + +error: + STREAM_STATUS(p_ctx) = status; + return status; +} + +/**************************************************************************//** + * Seek over data in the container. + * + * @param p_ctx The reader context. + * @param p_offset The seek offset. + * @param mode The seek mode. + * @param flags The seek flags. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_reader_seek( VC_CONTAINER_T *p_ctx, + int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_PARAM_UNUSED(p_ctx); + VC_CONTAINER_PARAM_UNUSED(p_offset); + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + + /* RTSP is a non-seekable container */ + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; +} + +/**************************************************************************//** + * Close the container. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +static VC_CONTAINER_STATUS_T rtsp_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[i]->priv->module; + + if (t_module->control_uri && t_module->session_header) + { + /* Send the teardown message and wait for a response, although it + * isn't important whether it was successful or not. */ + if (rtsp_send_teardown_request(p_ctx, t_module) == VC_CONTAINER_SUCCESS) + (void)rtsp_read_response(p_ctx); + } + + if (t_module->reader) + vc_container_close(t_module->reader); + if (t_module->reader_uri) + vc_uri_release(t_module->reader_uri); + if (t_module->control_uri) + free(t_module->control_uri); + if (t_module->session_header) + free(t_module->session_header); + vc_container_free_track(p_ctx, p_ctx->tracks[i]); /* Also need to close track's reader */ + } + p_ctx->tracks = NULL; + p_ctx->tracks_num = 0; + if (module) + { + if (module->comms_buffer) + free(module->comms_buffer); + if (module->header_list) + vc_containers_list_destroy(module->header_list); + free(module); + } + p_ctx->priv->module = 0; + return VC_CONTAINER_SUCCESS; +} + +/**************************************************************************//** + * Open the container. + * Uses the I/O URI and/or data to configure the container. + * + * @param p_ctx The reader context. + * @return The resulting status of the function. + */ +VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + uint32_t ii; + + /* Check the URI scheme looks valid */ + if (!vc_uri_scheme(p_ctx->priv->uri) || + (strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTSP_SCHEME) && + strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTSP_PKT_SCHEME))) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + if ((module = (VC_CONTAINER_MODULE_T *)malloc(sizeof(VC_CONTAINER_MODULE_T))) == NULL) + { + status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; + goto error; + } + + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks = module->tracks; + module->next_rtp_port = FIRST_DYNAMIC_PORT; + module->cseq_value = 0; + module->uri_has_network_info = + (strncasecmp(p_ctx->priv->io->uri, RTSP_NETWORK_URI_START, RTSP_NETWORK_URI_START_LENGTH) == 0); + module->comms_buffer = (char *)calloc(1, COMMS_BUFFER_SIZE+1); + if (!module->comms_buffer) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + /* header_list will contain pointers into the response_buffer, so take care in re-use */ + module->header_list = vc_containers_list_create(HEADER_LIST_INITIAL_CAPACITY, sizeof(RTSP_HEADER_T), + (VC_CONTAINERS_LIST_COMPARATOR_T)rtsp_header_comparator); + if (!module->header_list) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + + status = rtsp_describe(p_ctx); + for (ii = 0; status == VC_CONTAINER_SUCCESS && ii < p_ctx->tracks_num; ii++) + status = rtsp_setup(p_ctx, p_ctx->tracks[ii]->priv->module); + for (ii = 0; status == VC_CONTAINER_SUCCESS && ii < p_ctx->tracks_num; ii++) + status = rtsp_play(p_ctx, p_ctx->tracks[ii]->priv->module); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + /* Set the RTSP stream to block briefly, to allow polling for closure as well as to avoid spinning CPU */ + vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, DATA_UNAVAILABLE_READ_TIMEOUT_MS); + + p_ctx->priv->pf_close = rtsp_reader_close; + p_ctx->priv->pf_read = rtsp_reader_read; + p_ctx->priv->pf_seek = rtsp_reader_seek; + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) goto error; + return VC_CONTAINER_SUCCESS; + +error: + if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS) + status = VC_CONTAINER_ERROR_FORMAT_INVALID; + LOG_DEBUG(p_ctx, "error opening RTSP stream (%i)", status); + rtsp_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open rtsp_reader_open +#endif diff --git a/containers/rv9/CMakeLists.txt b/containers/rv9/CMakeLists.txt new file mode 100755 index 0000000..f65a7af --- /dev/null +++ b/containers/rv9/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_rv9 ${LIBRARY_TYPE} rv9_reader.c) + +target_link_libraries(reader_rv9 containers) + +install(TARGETS reader_rv9 DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/rv9/rv9_reader.c b/containers/rv9/rv9_reader.c new file mode 100755 index 0000000..cb4ca40 --- /dev/null +++ b/containers/rv9/rv9_reader.c @@ -0,0 +1,335 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +/****************************************************************************** +Defines. +******************************************************************************/ + +#define BI32(b) (((b)[0]<<24)|((b)[1]<<16)|((b)[2]<<8)|((b)[3])) +#define BI16(b) (((b)[0]<<8)|((b)[1])) + +#define FRAME_HEADER_LEN 20 +#define MAX_NUM_SEGMENTS 64 + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct { + uint32_t len; + uint32_t timestamp; + uint16_t sequence; + uint16_t flags; + uint32_t num_segments; + uint32_t seg_offset; +} RV9_FRAME_HEADER_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *track; + uint8_t mid_frame; + uint32_t frame_read; + uint32_t frame_len; + RV9_FRAME_HEADER_T hdr; + uint8_t data[FRAME_HEADER_LEN + (MAX_NUM_SEGMENTS<<3) + 1]; + uint32_t data_len; + uint8_t type; +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +static VC_CONTAINER_STATUS_T rv9_read_file_header(VC_CONTAINER_T *p_ctx, + VC_CONTAINER_TRACK_T *track) +{ + VC_CONTAINER_STATUS_T status; + VC_CONTAINER_FOURCC_T codec; + uint8_t dummy[12]; + uint32_t length; + + if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS; + + length = BI32(dummy); + if(length < 12 || length > 1024) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + if(dummy[4] != 'V' || dummy[5] != 'I' || dummy[6] != 'D' || dummy[7] != 'O' || + dummy[8] != 'R' || dummy[9] != 'V' || dummy[11] != '0') + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + switch(dummy[10]) { + case '4': codec = VC_CONTAINER_CODEC_RV40; break; + case '3': codec = VC_CONTAINER_CODEC_RV30; break; + case '2': codec = VC_CONTAINER_CODEC_RV20; break; + case '1': codec = VC_CONTAINER_CODEC_RV10; break; + default: return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + } + + if (!track) + return VC_CONTAINER_SUCCESS; + + status = vc_container_track_allocate_extradata(p_ctx, track, length); + if(status != VC_CONTAINER_SUCCESS) return status; + + if(READ_BYTES(p_ctx, track->format->extradata, length) != length) return VC_CONTAINER_ERROR_EOS; + track->format->extradata_size = length; + + track->format->codec = codec; + return STREAM_STATUS(p_ctx); +} + +static VC_CONTAINER_STATUS_T rv9_read_frame_header(VC_CONTAINER_T *p_ctx) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t seg_offset = (uint32_t) -1; + uint8_t *buffer = module->data + FRAME_HEADER_LEN; + + if(READ_BYTES(p_ctx, module->data, FRAME_HEADER_LEN) != FRAME_HEADER_LEN) return VC_CONTAINER_ERROR_EOS; + module->data_len = FRAME_HEADER_LEN; + + module->hdr.len = BI32(module->data); + module->hdr.timestamp = BI32(module->data+4); + module->hdr.sequence = BI16(module->data+8); + module->hdr.flags = BI16(module->data+10); + module->hdr.num_segments = BI32(module->data+16); + + module->frame_len = FRAME_HEADER_LEN + (module->hdr.num_segments * 8) + module->hdr.len; + + // if we have space, we store up the segments in memory so we can tell the frame + // type, since most streams have their type byte as the first follow the segment information. + // if we don't have space, then we just don't know the frame type, so will not emit timestamp + // information as we don't know if it's reliable. + if(module->hdr.num_segments <= MAX_NUM_SEGMENTS) + { + uint32_t i; + + if(READ_BYTES(p_ctx, buffer, 8*module->hdr.num_segments) != 8*module->hdr.num_segments) return VC_CONTAINER_ERROR_EOS; + module->data_len += (module->hdr.num_segments * 8); + + for (i=0; ihdr.num_segments; i++) + { + uint32_t valid_seg; + uint32_t offset; + + valid_seg = BI32(buffer); + offset = BI32(buffer+4); + + if (valid_seg && seg_offset > offset) + seg_offset = offset; + + // this boolean field should have only 0 or 1 values + if(valid_seg > 1) return VC_CONTAINER_ERROR_FORMAT_INVALID; + + buffer += 8; + } + } + + if(seg_offset == 0) + { + if (READ_BYTES(p_ctx, buffer, 1) != 1) return VC_CONTAINER_ERROR_EOS; + module->data_len += 1; + + module->type = (*buffer >> 5) & 3; + } + else + module->type = (uint8_t) -1; + + return VC_CONTAINER_SUCCESS; +} + +static uint32_t rv9_get_frame_data(VC_CONTAINER_T *p_ctx, uint32_t len, uint8_t *dest) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t ret = 0; + + // we may have read some data before into the data array, so + // check whether we've copied all this data out first. + if(module->frame_read < module->data_len) + { + uint32_t copy = MIN(len, module->data_len - module->frame_read); + if(dest) + { + memcpy(dest, module->data + module->frame_read, copy); + dest += copy; + } + ret += copy; + len -= copy; + } + + // if there is still more to do, we need to access the IO to do this. + if(len > 0) + { + if(dest) + ret += READ_BYTES(p_ctx, dest, len); + else + ret += SKIP_BYTES(p_ctx, len); + } + + module->frame_read += ret; + return ret; +} + +/***************************************************************************** +Functions exported as part of the Container Module API +*****************************************************************************/ +static VC_CONTAINER_STATUS_T rv9_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *packet, + uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_STATUS_T status; + unsigned int size; + + if(!module->mid_frame) + { + if((status = rv9_read_frame_header(p_ctx)) != VC_CONTAINER_SUCCESS) return status; + + module->mid_frame = 1; + module->frame_read = 0; + } + + packet->size = module->frame_len; + packet->pts = module->type < 3 ? module->hdr.timestamp * 1000LL : VC_CONTAINER_TIME_UNKNOWN; + packet->dts = packet->pts; + packet->track = 0; + packet->flags = module->type < 2 ? VC_CONTAINER_PACKET_FLAG_KEYFRAME : 0; + if(module->frame_read == 0) + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + size = rv9_get_frame_data(p_ctx, module->frame_len - module->frame_read, NULL); + if(module->frame_read == module->frame_len) + { + module->frame_read = 0; + module->mid_frame = 0; + } + return STREAM_STATUS(p_ctx); + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + size = MIN(module->frame_len - module->frame_read, packet->buffer_size); + size = rv9_get_frame_data(p_ctx, size, packet->data); + if(module->frame_read == module->frame_len) + { + module->frame_read = 0; + module->mid_frame = 0; + packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + } + packet->size = size; + + return size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rv9_reader_seek( VC_CONTAINER_T *p_ctx, + int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, + VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + VC_CONTAINER_PARAM_UNUSED(flags); + + if(*offset == 0LL && mode == VC_CONTAINER_SEEK_MODE_TIME) + { + SEEK(p_ctx, module->track->format->extradata_size); + module->mid_frame = 0; + module->frame_read = 0; + return STREAM_STATUS(p_ctx); + } + else + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T rv9_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--) + vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Check the file header */ + if(rv9_read_file_header(p_ctx, 0) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks_num = 1; + p_ctx->tracks = &module->track; + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_RV40; + p_ctx->tracks[0]->is_enabled = true; + + if((status = rv9_read_file_header(p_ctx, p_ctx->tracks[0])) != VC_CONTAINER_SUCCESS) goto error; + + LOG_DEBUG(p_ctx, "using rv9 reader"); + + p_ctx->priv->pf_close = rv9_reader_close; + p_ctx->priv->pf_read = rv9_reader_read; + p_ctx->priv->pf_seek = rv9_reader_seek; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "rv9: error opening stream (%i)", status); + if(module) rv9_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function +********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open rv9_reader_open +#endif diff --git a/containers/simple/CMakeLists.txt b/containers/simple/CMakeLists.txt new file mode 100755 index 0000000..0fbdc11 --- /dev/null +++ b/containers/simple/CMakeLists.txt @@ -0,0 +1,18 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_simple ${LIBRARY_TYPE} simple_reader.c) + +target_link_libraries(reader_simple containers) + +install(TARGETS reader_simple DESTINATION ${VMCS_PLUGIN_DIR}) + +add_library(writer_simple ${LIBRARY_TYPE} simple_writer.c) + +target_link_libraries(writer_simple containers) + +install(TARGETS writer_simple DESTINATION ${VMCS_PLUGIN_DIR}) diff --git a/containers/simple/simple_common.h b/containers/simple/simple_common.h new file mode 100755 index 0000000..b75242c --- /dev/null +++ b/containers/simple/simple_common.h @@ -0,0 +1,42 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef SIMPLE_COMMON_H +#define SIMPLE_COMMON_H + +#define SIGNATURE_STRING "S1MPL3" +#define SIGNATURE_END_STRING "3LPM1S" + +/** List of configuration options supported in the header */ +#define CONFIG_VARIANT "VARIANT" +#define CONFIG_URI "URI" +#define CONFIG_CODEC_VARIANT "CODEC_VARIANT" +#define CONFIG_BITRATE "BITRATE" +#define CONFIG_UNFRAMED "UNFRAMED" +#define CONFIG_VIDEO_CROP "VIDEO_CROP" +#define CONFIG_VIDEO_ASPECT "VIDEO_ASPECT" + +#endif /* SIMPLE_COMMON_H */ diff --git a/containers/simple/simple_reader.c b/containers/simple/simple_reader.c new file mode 100755 index 0000000..f0ef55a --- /dev/null +++ b/containers/simple/simple_reader.c @@ -0,0 +1,599 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +#include "simple_common.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define MAX_LINE_SIZE 512 +#define LINE_PADDING 3 /* 2 for newline + 1 for null */ + +#define MAX_TRACKS 4 +#define MAX_HEADER_LINES 512 + +typedef enum SIMPLE_VARIANT_T +{ + VARIANT_DEFAULT = 0, + VARIANT_MMAL, + VARIANT_OMX +} SIMPLE_VARIANT_T; + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct SIMPLE_PACKET_STATE_T +{ + unsigned int track_num; + unsigned int flags; + + uint64_t metadata_offset; /* Offset in metadata stream */ + uint32_t data_size; /* Size of current data packet */ + uint32_t data_left; /* Data left to read in current packet */ + + int64_t pts; + +} SIMPLE_PACKET_STATE_T; + +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + SIMPLE_PACKET_STATE_T *state; + SIMPLE_PACKET_STATE_T local_state; + + VC_CONTAINER_IO_T *io; + uint64_t data_offset; /* Current offset in data stream */ + char uri[MAX_LINE_SIZE+1]; + + SIMPLE_VARIANT_T variant; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + VC_CONTAINER_TRACK_T *tracks[MAX_TRACKS]; + + char line[MAX_LINE_SIZE + LINE_PADDING]; + + int64_t metadata_offset; + + /* Shared packet state. This is used when the tracks are in sync, + * and for the track at the earliest position in the file when they are + * not in sync */ + SIMPLE_PACKET_STATE_T state; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T simple_read_line( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + unsigned int i, bytes = PEEK_BYTES(ctx, module->line, sizeof(module->line)-1); + + if (!bytes) + return VC_CONTAINER_ERROR_EOS; + + /* Find new-line marker */ + for (i = 0; i < bytes; i++) + if (module->line[i] == '\n') + break; + + /* Bail out if line is bigger than the maximum allowed */ + if (i == sizeof(module->line)-1) + { + LOG_ERROR(ctx, "line too big"); + return VC_CONTAINER_ERROR_CORRUPTED; + } + + if (i < bytes) + { + module->line[i++] = 0; + if (i < bytes && module->line[i] == '\r') + i++; + } + module->line[i] = 0; /* Make sure the line is null terminated */ + + SKIP_BYTES(ctx, i); + return VC_CONTAINER_SUCCESS; +} + +static VC_CONTAINER_STATUS_T simple_read_header( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_TRACK_T *track = NULL; + VC_CONTAINER_FOURCC_T fourcc; + int matches, width, height, channels, samplerate, bps, blockalign, value; + unsigned int lines = 1; + + /* Skip the signature */ + if (simple_read_line(ctx) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_CORRUPTED; + + while (lines++ < MAX_HEADER_LINES && + simple_read_line(ctx) == VC_CONTAINER_SUCCESS) + { + /* Our exit condition is the end signature */ + if (!memcmp(module->line, SIGNATURE_END_STRING, sizeof(SIGNATURE_STRING)-1)) + { + if (track) ctx->tracks[ctx->tracks_num++] = track; + return VC_CONTAINER_SUCCESS; + } + + /* Start of track description */ + if (!memcmp(module->line, "TRACK ", sizeof("TRACK ")-1)) + { + /* Add track we were constructing */ + if (track) ctx->tracks[ctx->tracks_num++] = track; + track = NULL; + + if (ctx->tracks_num >= MAX_TRACKS) + { + LOG_ERROR(ctx, "too many tracks, ignoring: %s", module->line); + continue; + } + track = vc_container_allocate_track(ctx, sizeof(*track->priv->module)); + if (!track) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + track->is_enabled = true; + track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + + if ((matches = sscanf(module->line, + "TRACK video, %4c, %i, %i", + (char *)&fourcc, &width, &height)) > 0) + { + track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; + track->format->codec = fourcc; + if (matches > 1) track->format->type->video.width = width; + if (matches > 2) track->format->type->video.height = height; + } + else if ((matches = sscanf(module->line, + "TRACK audio, %4c, %i, %i, %i, %i", + (char *)&fourcc, &channels, &samplerate, &bps, + &blockalign)) > 0) + { + track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + track->format->codec = fourcc; + if (matches > 1) track->format->type->audio.channels = channels; + if (matches > 2) track->format->type->audio.sample_rate = samplerate; + if (matches > 3) track->format->type->audio.bits_per_sample = bps; + if (matches > 4) track->format->type->audio.block_align = blockalign; + } + if ((matches = sscanf(module->line, + "TRACK subpicture, %4c, %i", + (char *)&fourcc, &value)) > 0) + { + track->format->es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE; + track->format->codec = fourcc; + if (matches > 1) track->format->type->subpicture.encoding = value; + } + } + + if (!track) + continue; /* Nothing interesting */ + + /* VARIANT of the syntax */ + if (sscanf(module->line, CONFIG_VARIANT" %i", &value) == 1) + { + track->priv->module->variant = value; + LOG_FORMAT(ctx, CONFIG_VARIANT": %i", value); + } + /* URI for elementary stream */ + else if (sscanf(module->line, CONFIG_URI" %s", track->priv->module->uri) == 1) + LOG_FORMAT(ctx, CONFIG_URI": %s", track->priv->module->uri); + /* COCDEC_VARIANT of elementary stream */ + else if (sscanf(module->line, CONFIG_CODEC_VARIANT" %4c", (char *)&fourcc) == 1) + { + track->format->codec_variant = fourcc; + LOG_FORMAT(ctx, CONFIG_CODEC_VARIANT": %4.4s", (char *)&fourcc); + } + /* BITRATE of elementary stream */ + else if (sscanf(module->line, CONFIG_BITRATE" %i", &value) == 1) + { + track->format->bitrate = value; + LOG_FORMAT(ctx, CONFIG_BITRATE": %i", value); + } + /* UNFRAMED elementary stream */ + else if (!memcmp(module->line, CONFIG_UNFRAMED, sizeof(CONFIG_UNFRAMED)-1)) + { + track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED; + LOG_FORMAT(ctx, CONFIG_UNFRAMED); + } + /* VIDEO_CROP information */ + else if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && + sscanf(module->line, CONFIG_VIDEO_CROP" %i, %i", &width, &height) == 2) + { + track->format->type->video.visible_width = width; + track->format->type->video.visible_height = height; + LOG_FORMAT(ctx, CONFIG_VIDEO_CROP": %i, %i", width, height); + } + /* VIDEO_ASPECT information */ + else if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO && + sscanf(module->line, CONFIG_VIDEO_ASPECT" %i, %i", &width, &height) == 2) + { + track->format->type->video.par_num = width; + track->format->type->video.par_den = height; + LOG_FORMAT(ctx, CONFIG_VIDEO_ASPECT": %i, %i", width, height); + } + } + + if (track) vc_container_free_track(ctx, track); + return VC_CONTAINER_ERROR_CORRUPTED; +} + +static uint32_t simple_convert_packet_flags(VC_CONTAINER_T *ctx, + unsigned int track_num, uint32_t flags) +{ + typedef struct { uint32_t from; uint32_t to; } convert_from_t; + const convert_from_t convert_from_mmal[] = + { {1<<1, VC_CONTAINER_PACKET_FLAG_FRAME_START}, + {1<<2, VC_CONTAINER_PACKET_FLAG_FRAME_END}, + {1<<3, VC_CONTAINER_PACKET_FLAG_KEYFRAME}, + {1<<4, VC_CONTAINER_PACKET_FLAG_DISCONTINUITY}, + {1<<5, VC_CONTAINER_PACKET_FLAG_CONFIG}, + {1<<6, VC_CONTAINER_PACKET_FLAG_ENCRYPTED}, + {0, 0} }; + const convert_from_t convert_from_omx[] = + { {0x10, VC_CONTAINER_PACKET_FLAG_FRAME_END}, + {0x20, VC_CONTAINER_PACKET_FLAG_KEYFRAME}, + {0x80, VC_CONTAINER_PACKET_FLAG_CONFIG}, + {0, 0} }; + const convert_from_t *convert_from = NULL; + int i; + + switch (ctx->tracks[track_num]->priv->module->variant) + { + case VARIANT_MMAL: convert_from = convert_from_mmal; break; + case VARIANT_OMX: convert_from = convert_from_omx; break; + default: break; + } + + if (convert_from) + { + uint32_t new_flags = 0; + for (i = 0; convert_from[i].from; i++) + if (convert_from[i].from & flags) + new_flags |= convert_from[i].to; + return new_flags; + } + + return flags; +} + +static int64_t simple_convert_packet_pts(VC_CONTAINER_T *ctx, + unsigned int track_num, int64_t pts, uint32_t flags) +{ + if (ctx->tracks[track_num]->priv->module->variant == VARIANT_OMX && + flags & 0x100) + return VC_CONTAINER_TIME_UNKNOWN; + + return pts; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_reader_read( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + VC_CONTAINER_TRACK_MODULE_T *track_module; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + SIMPLE_PACKET_STATE_T *state; + + /* If a specific track has been selected, use the track packet state */ + if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) + state = ctx->tracks[packet->track]->priv->module->state; + else + state = &module->state; + + /* Switch to the next packet when the current one is empty */ + if (!state->data_left) + { + unsigned int track_num, size; + int64_t pts; + int flags; + + SEEK(ctx, state->metadata_offset); + status = simple_read_line(ctx); + if (status != VC_CONTAINER_SUCCESS) + return status; + + if (sscanf(module->line, "%u %u %"PRIi64" %i", + &track_num, &size, &pts, &flags) != 4 && + (track_num = 0, sscanf(module->line, "%u %"PRIi64" %i", + &size, &pts, &flags)) != 3) + { + LOG_ERROR(ctx, "invalid metadata: %s", module->line); + return VC_CONTAINER_ERROR_CORRUPTED; + } + state->metadata_offset = STREAM_POSITION(ctx); + + if (track_num >= ctx->tracks_num) + { + LOG_DEBUG(ctx, "skipping %i bytes for track %d/%d", + size, track_num, ctx->tracks_num); + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* If we are reading from the global state (i.e. normal read or forced + read from the track on the global state), and the track we found is + not on the global state, reconnect the two */ + if (state == &module->state && + ctx->tracks[track_num]->priv->module->state != &module->state) + { + LOG_DEBUG(ctx, "reconnect track %u to the global state", track_num); + ctx->tracks[track_num]->priv->module->state = &module->state; + module->state = ctx->tracks[track_num]->priv->module->local_state; + return VC_CONTAINER_ERROR_CONTINUE; + } + + state->data_size = state->data_left = size; + state->track_num = track_num; + state->flags = simple_convert_packet_flags(ctx, track_num, flags); + state->pts = simple_convert_packet_pts(ctx, track_num, pts, flags); + + /* Discard empty packets */ + if (!state->data_size && !state->flags) + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* If there is data from another track skip past it */ + if ((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && + state->track_num != packet->track) + { + LOG_DEBUG(ctx, "skipping track %d/%d as we are ignoring it", + state->track_num, ctx->tracks_num); + + track_module = ctx->tracks[packet->track]->priv->module; + + /* Handle disconnection from global state */ + if (state == &module->state && + ctx->tracks[state->track_num]->priv->module->state == &module->state) + { + /* Make a copy of the global state */ + LOG_DEBUG(ctx, "using local state on track %d", packet->track); + track_module->local_state = module->state; + track_module->state = &track_module->local_state; + } + + track_module->state->data_left = 0; + return VC_CONTAINER_ERROR_CONTINUE; + } + + /* + * From this point we know we have the packet which was requested + */ + + /* !!!! If we aren't in the right position in the file go there now. */ + + track_module = ctx->tracks[state->track_num]->priv->module; + packet->track = state->track_num; + packet->size = state->data_left; + packet->frame_size = (state->flags & VC_CONTAINER_PACKET_FLAG_FRAME) ? + state->data_size : 0; + packet->flags = state->flags; + packet->pts = state->pts; + packet->dts = VC_CONTAINER_TIME_UNKNOWN; + if (state->data_left != state->data_size) + packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_START; + + if (flags & VC_CONTAINER_READ_FLAG_SKIP) + { + track_module->data_offset += state->data_left; + state->data_left = 0; + return VC_CONTAINER_SUCCESS; + } + + if (flags & VC_CONTAINER_READ_FLAG_INFO) + { + return VC_CONTAINER_SUCCESS; + } + + /* Now try to read data into buffer */ + vc_container_io_seek(track_module->io, track_module->data_offset); + + packet->size = vc_container_io_read(track_module->io, packet->data, + MIN(packet->buffer_size, state->data_left)); + state->data_left -= packet->size; + track_module->data_offset += packet->size; + + if (state->data_left) + packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END; + + return track_module->io->status; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_reader_seek( VC_CONTAINER_T *ctx, int64_t *offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_PARAM_UNUSED(ctx); + VC_CONTAINER_PARAM_UNUSED(offset); + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_reader_close( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + + for (; ctx->tracks_num > 0; ctx->tracks_num--) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[ctx->tracks_num-1]; + if (track->priv->module->io) + vc_container_io_close(track->priv->module->io); + vc_container_free_track(ctx, track); + } + + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + uint8_t h[sizeof(SIGNATURE_STRING)]; + unsigned int i; + + /* Check for the signature */ + if (PEEK_BYTES(ctx, h, sizeof(h)) != sizeof(h) || + memcmp(h, SIGNATURE_STRING, sizeof(SIGNATURE_STRING)-1)) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + LOG_DEBUG(ctx, "using simple reader"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if (!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + memset(module, 0, sizeof(*module)); + ctx->priv->module = module; + ctx->tracks = module->tracks; + + status = simple_read_header(ctx); + if (status != VC_CONTAINER_SUCCESS) + goto error; + + /* Open all the elementary streams */ + for (i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + char *uri; + + track->priv->module->io = vc_container_io_open(track->priv->module->uri, + VC_CONTAINER_IO_MODE_READ, &status); + + /* URI might be relative to the path of the metadata file so + * try again with that new path */ + if (!track->priv->module->io && + (uri = malloc(strlen(ctx->priv->io->uri) + + strlen(track->priv->module->uri) + 1)) != NULL) + { + char *end; + + strcpy(uri, ctx->priv->io->uri); + + /* Find the last directory separator */ + for (end = uri + strlen(ctx->priv->io->uri) + 1; end != uri; end--) + if (*(end-1) == '/' || *(end-1) == '\\') + break; + strcpy(end, track->priv->module->uri); + + track->priv->module->io = vc_container_io_open(uri, + VC_CONTAINER_IO_MODE_READ, &status); + if (!track->priv->module->io) + LOG_ERROR(ctx, "could not open elementary stream: %s", uri); + free(uri); + } + if (!track->priv->module->io) + { + LOG_ERROR(ctx, "could not open elementary stream: %s", + track->priv->module->uri); + goto error; + } + } + + /* + * We now have all the information we really need to start playing the stream + */ + + module->metadata_offset = STREAM_POSITION(ctx); + + /* Initialise state for all tracks */ + module->state.metadata_offset = module->metadata_offset; + for (i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + track->priv->module->state = &module->state; + } + + /* Look for the codec configuration data for each track so + * we can store it in the track format */ + for (i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + VC_CONTAINER_PACKET_T packet; + packet.track = i; + status = VC_CONTAINER_ERROR_CONTINUE; + + while (status == VC_CONTAINER_ERROR_CONTINUE) + status = simple_reader_read(ctx, &packet, + VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_FORCE_TRACK); + if (status != VC_CONTAINER_SUCCESS) + continue; + + status = vc_container_track_allocate_extradata(ctx, track, packet.size); + if (status != VC_CONTAINER_SUCCESS) + continue; + + packet.data = track->format->extradata; + packet.buffer_size = packet.size; + packet.size = 0; + status = simple_reader_read(ctx, &packet, + VC_CONTAINER_READ_FLAG_FORCE_TRACK); + if (status != VC_CONTAINER_SUCCESS) + continue; + + track->format->extradata_size = packet.size; + } + + ctx->priv->pf_close = simple_reader_close; + ctx->priv->pf_read = simple_reader_read; + ctx->priv->pf_seek = simple_reader_seek; + return VC_CONTAINER_SUCCESS; + + error: + LOG_ERROR(ctx, "simple: error opening stream (%i)", status); + simple_reader_close(ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open simple_reader_open +#endif diff --git a/containers/simple/simple_writer.c b/containers/simple/simple_writer.c new file mode 100755 index 0000000..e0cf0f5 --- /dev/null +++ b/containers/simple/simple_writer.c @@ -0,0 +1,348 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include + +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" + +#include "simple_common.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define MAX_TRACKS 4 +#define MAX_LINE_SIZE 512 + +#define ES_SUFFIX "%s.%2.2i.%4.4s" +#define ES_SUFFIX_SIZE 8 + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_TRACK_MODULE_T +{ + VC_CONTAINER_IO_T *io; + char *uri; + + bool config_done; + +} VC_CONTAINER_TRACK_MODULE_T; + +typedef struct VC_CONTAINER_MODULE_T +{ + char line[MAX_LINE_SIZE + 1]; + + VC_CONTAINER_TRACK_T *tracks[MAX_TRACKS]; + bool header_done; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T * ); +static VC_CONTAINER_STATUS_T simple_writer_write( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *packet ); + +/****************************************************************************** +Local Functions +******************************************************************************/ +static VC_CONTAINER_STATUS_T simple_write_line( VC_CONTAINER_T *ctx, + const char *format, ...) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + va_list args; + int result; + + va_start(args, format); + result = vsnprintf(module->line, sizeof(module->line), format, args); + va_end(args); + + if (result >= (int)sizeof(module->line)) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + WRITE_BYTES(ctx, module->line, result); + _WRITE_U8(ctx, '\n'); + return STREAM_STATUS(ctx); +} + +static VC_CONTAINER_STATUS_T simple_write_header( VC_CONTAINER_T *ctx ) +{ + unsigned int i; + + simple_write_line(ctx, SIGNATURE_STRING); + + for (i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + + if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + simple_write_line(ctx, "TRACK video, %4.4s, %i, %i", + (char *)&track->format->codec, + (int)track->format->type->video.width, + (int)track->format->type->video.height); + if ((track->format->type->video.visible_width && + track->format->type->video.visible_width != + track->format->type->video.width) || + (track->format->type->video.visible_height && + track->format->type->video.visible_height != + track->format->type->video.height)) + simple_write_line(ctx, CONFIG_VIDEO_CROP" %i, %i", + track->format->type->video.visible_width, + track->format->type->video.visible_height); + if (track->format->type->video.par_num && + track->format->type->video.par_den) + simple_write_line(ctx, CONFIG_VIDEO_ASPECT" %i, %i", + track->format->type->video.par_num, + track->format->type->video.par_den); + } + else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { + simple_write_line(ctx, "TRACK audio, %4.4s, %i, %i, %i, %i", + (char *)&track->format->codec, + (int)track->format->type->audio.channels, + (int)track->format->type->audio.sample_rate, + (int)track->format->type->audio.bits_per_sample, + (int)track->format->type->audio.block_align); + } + else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) + { + simple_write_line(ctx, "TRACK subpicture, %4.4s, %i", + (char *)&track->format->codec, + (int)track->format->type->subpicture.encoding); + } + else + { + simple_write_line(ctx, "TRACK unknown, %4.4s", + (char *)&track->format->codec); + } + + simple_write_line(ctx, CONFIG_URI" %s", track->priv->module->io->uri); + if (track->format->codec_variant) + simple_write_line(ctx, CONFIG_CODEC_VARIANT" %4.4s", + (char *)&track->format->codec_variant); + if (track->format->bitrate) + simple_write_line(ctx, CONFIG_BITRATE" %i", track->format->bitrate); + if (!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + simple_write_line(ctx, CONFIG_UNFRAMED); + } + + simple_write_line(ctx, SIGNATURE_END_STRING); + + ctx->priv->module->header_done = true; + return STREAM_STATUS(ctx); +} + +static VC_CONTAINER_STATUS_T simple_write_config( VC_CONTAINER_T *ctx, + unsigned int track_num, VC_CONTAINER_PACKET_T *pkt) +{ + VC_CONTAINER_TRACK_T *track = ctx->tracks[track_num]; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_PACKET_T packet; + + track->priv->module->config_done = true; + + if (track->format->extradata_size) + { + packet.size = track->format->extradata_size; + packet.data = track->format->extradata; + packet.track = track_num; + packet.pts = pkt ? pkt->pts : VC_CONTAINER_TIME_UNKNOWN; + packet.flags = 0; + packet.flags |= VC_CONTAINER_PACKET_FLAG_CONFIG; + + status = simple_writer_write(ctx, &packet); + } + + return status; +} + +static VC_CONTAINER_STATUS_T simple_write_add_track( VC_CONTAINER_T *ctx, + VC_CONTAINER_ES_FORMAT_T *format ) +{ + VC_CONTAINER_TRACK_T *track = NULL; + VC_CONTAINER_STATUS_T status; + const char *uri = vc_uri_path(ctx->priv->uri); + unsigned int uri_size = strlen(uri); + + /* Allocate and initialise track data */ + if (ctx->tracks_num >= MAX_TRACKS) + return VC_CONTAINER_ERROR_OUT_OF_RESOURCES; + + ctx->tracks[ctx->tracks_num] = track = + vc_container_allocate_track(ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T) + + uri_size + ES_SUFFIX_SIZE + 1); + if (!track) + return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + if (format->extradata_size) + { + status = vc_container_track_allocate_extradata(ctx, track, format->extradata_size); + if (status != VC_CONTAINER_SUCCESS) + goto error; + } + vc_container_format_copy(track->format, format, format->extradata_size); + + track->priv->module->uri = (char *)&track->priv->module[1]; + snprintf(track->priv->module->uri, uri_size + ES_SUFFIX_SIZE + 1, + ES_SUFFIX, uri, ctx->tracks_num, (char *)&track->format->codec); + + LOG_DEBUG(ctx, "opening elementary stream: %s", track->priv->module->uri); + track->priv->module->io = vc_container_io_open(track->priv->module->uri, + VC_CONTAINER_IO_MODE_WRITE, &status); + if (status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(ctx, "error opening elementary stream: %s", + track->priv->module->uri); + goto error; + } + + ctx->tracks_num++; + return VC_CONTAINER_SUCCESS; + + error: + if (track) + vc_container_free_track(ctx, track); + return status; +} + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_writer_close( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_MODULE_T *module = ctx->priv->module; + for (; ctx->tracks_num > 0; ctx->tracks_num--) + { + vc_container_io_close(ctx->tracks[ctx->tracks_num-1]->priv->module->io); + vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]); + } + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_writer_write( VC_CONTAINER_T *ctx, + VC_CONTAINER_PACKET_T *packet ) +{ + VC_CONTAINER_STATUS_T status; + + if (!ctx->priv->module->header_done) + { + status = simple_write_header(ctx); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + if (!ctx->tracks[packet->track]->priv->module->config_done) + { + status = simple_write_config(ctx, packet->track, packet); + if (status != VC_CONTAINER_SUCCESS) + return status; + } + + /* Write the metadata */ + status = simple_write_line(ctx, "%i %i %"PRIi64" 0x%x", + (int)packet->track, (int)packet->size, packet->pts, packet->flags); + if (status != VC_CONTAINER_SUCCESS) + return status; + + /* Write the elementary stream */ + vc_container_io_write(ctx->tracks[packet->track]->priv->module->io, + packet->data, packet->size); + + return STREAM_STATUS(ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T simple_writer_control( VC_CONTAINER_T *ctx, + VC_CONTAINER_CONTROL_T operation, va_list args ) +{ + VC_CONTAINER_ES_FORMAT_T *format; + + switch (operation) + { + case VC_CONTAINER_CONTROL_TRACK_ADD: + format = (VC_CONTAINER_ES_FORMAT_T *)va_arg(args, VC_CONTAINER_ES_FORMAT_T *); + return simple_write_add_track(ctx, format); + + case VC_CONTAINER_CONTROL_TRACK_ADD_DONE: + simple_write_header( ctx ); + return VC_CONTAINER_SUCCESS; + + default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; + } +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T *ctx ) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID; + const char *extension = vc_uri_path_extension(ctx->priv->uri); + VC_CONTAINER_MODULE_T *module; + + /* Check if the user has specified a container */ + vc_uri_find_query(ctx->priv->uri, 0, "container", &extension); + + /* Check we're the right writer for this */ + if(!extension) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if(strcasecmp(extension, "smpl") && strcasecmp(extension, "simple")) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + LOG_DEBUG(ctx, "using simple writer"); + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + ctx->priv->module = module; + ctx->tracks = module->tracks; + + ctx->priv->pf_close = simple_writer_close; + ctx->priv->pf_write = simple_writer_write; + ctx->priv->pf_control = simple_writer_control; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(ctx, "simple: error opening stream (%i)", status); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak writer_open simple_writer_open +#endif diff --git a/containers/test/CMakeLists.txt b/containers/test/CMakeLists.txt new file mode 100755 index 0000000..7d36352 --- /dev/null +++ b/containers/test/CMakeLists.txt @@ -0,0 +1,66 @@ +# Generate test application +add_executable(containers_test test.c) +target_link_libraries(containers_test -Wl,--no-whole-archive containers) +install(TARGETS containers_test DESTINATION bin) + +# Generate test application +add_executable(containers_check_frame_int check_frame_int.c) +target_link_libraries(containers_check_frame_int -Wl,--no-whole-archive containers) +install(TARGETS containers_check_frame_int DESTINATION bin) + +# Generate autotest application +#add_executable(containers_autotest autotest.cpp crc_32.c) +#target_link_libraries(containers_autotest -Wl,--no-whole-archive containers}) +#install(TARGETS containers_autotest DESTINATION bin) + +# Helper code to provide non-blocking console input +if (WIN32) +set( NB_IO_SOURCE nb_io_win32.c ) +elseif (UNIX) +set( NB_IO_SOURCE nb_io_unix.c ) +endif (WIN32) +set(extra_test_SRCS nb_io_win32.c autotest.cpp crc_32.c) +add_custom_target(containers_test_extra + COMMAND touch ${extra_test_SRCS} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/containers/test) +add_dependencies(containers_test containers_test_extra) + +# Generate net test applications +add_executable(containers_stream_client stream_client.c ${NB_IO_SOURCE}) +target_link_libraries(containers_stream_client containers) +install(TARGETS containers_stream_client DESTINATION bin) + +add_executable(containers_stream_server stream_server.c) +target_link_libraries(containers_stream_server containers) +install(TARGETS containers_stream_server DESTINATION bin) + +add_executable(containers_datagram_sender datagram_sender.c) +target_link_libraries(containers_datagram_sender containers) +install(TARGETS containers_datagram_sender DESTINATION bin) + +add_executable(containers_datagram_receiver datagram_receiver.c) +target_link_libraries(containers_datagram_receiver containers) +install(TARGETS containers_datagram_receiver DESTINATION bin) + +add_executable(containers_rtp_decoder rtp_decoder.c ${NB_IO_SOURCE}) +target_link_libraries(containers_rtp_decoder containers) +install(TARGETS containers_rtp_decoder DESTINATION bin) + +# Generate URI test application +add_executable(containers_test_uri test_uri.c) +target_link_libraries(containers_test_uri containers) +install(TARGETS containers_test_uri DESTINATION bin) + +# Generate URI pipe application +add_executable(containers_uri_pipe uri_pipe.c ${NB_IO_SOURCE}) +target_link_libraries(containers_uri_pipe containers) +install(TARGETS containers_uri_pipe DESTINATION bin) + +# Generate bit stream test application +add_executable(containers_test_bits test_bits.c) +target_link_libraries(containers_test_bits containers) +install(TARGETS containers_test_bits DESTINATION bin) + +# Generate packet file dump application +add_executable(containers_dump_pktfile dump_pktfile.c) +install(TARGETS containers_dump_pktfile DESTINATION bin) diff --git a/containers/test/autotest.cpp b/containers/test/autotest.cpp new file mode 100755 index 0000000..2a9a3cb --- /dev/null +++ b/containers/test/autotest.cpp @@ -0,0 +1,1958 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// MS compilers require __cdecl calling convention on some callbacks. Other compilers reject it. +#if (!defined(_MSC_VER) && !defined(__cdecl)) +#define __cdecl +#endif + +extern "C" +{ +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_io.h" + +// Declare the CRC32 function from Snippets.org (obtained from the wayback machine, see crc_32.c) + uint32_t crc32buf(const uint8_t *buf, size_t len); +} + +// Error logger. It looks a little like std::cout, but it will stop the program if required on an end-of-line, +// returning an error to the caller. This is intended to allow automated tests to run to first fail, +// but interactive tests to run all the way through (using -k) +class ERROR_LOGGER_T +{ +public: + ERROR_LOGGER_T(): error_is_fatal(true), had_any_error(false) + {} + + ~ERROR_LOGGER_T() + { + // Flush out any bits of message that have been assembled, but not yet flushed out. + std::cerr << stream.str(); + + if (had_any_error) + { + exit(VC_CONTAINER_ERROR_FAILED); + } + } + + // Tell the logger that we're going to keep going after an error if we possibly can + void set_errors_not_fatal() + { + error_is_fatal = false; + } + + // Tell the error logger to redirect its output to this stream instead of the default stderr. + // This is used when dumping is enabled, so that errors become part of the stream description. + void set_output(std::ostream* new_stream) + { + output_stream = new_stream; + } + + // Generic inserter. Always calls through to the inserter for the base class. + template ERROR_LOGGER_T& operator<<(const T& object) + { + stream << object; + had_any_error = true; + return *this; + } + + // specialised inserter for iomanip type objects. Ensures that this object, and not the contained stream, + // is passed to the object. + ERROR_LOGGER_T& operator<<(ERROR_LOGGER_T& (__cdecl *_Pfn)(ERROR_LOGGER_T&)) + { + return _Pfn(*this); + } + + // implementation of flush. This is called by endl, and if the flags are set the program will stop. + ERROR_LOGGER_T& flush() + { + // If required send it to the quoted stream + if (output_stream) + { + *output_stream << stream.str(); + } + else + { + // just send it to stderr + std::cerr << stream.str(); + } + + stream.clear(); // reset any odd flags + stream.str(""); // empty the string + + if (error_is_fatal) + { + exit(VC_CONTAINER_ERROR_FAILED); + } + + return *this; + } + +private: + // set true if should stop on first error (usual for smoke testing) + // or keep going (usual if you want to look at the logs). Controlled by config -k flag (as for make) + bool error_is_fatal; + + // Set true if we've ever had an error. This way the app will return an error even if it kept going after it. + bool had_any_error; + + // The current error message + std::ostringstream stream; + + // The output stream, if not defaulted + std::ostream* output_stream; +}; + +namespace std +{ + // GCC insists I say this twice: + ERROR_LOGGER_T& ends(ERROR_LOGGER_T& logger); + + // implementation of std::ends for the error logger - flushes the message. + ERROR_LOGGER_T& ends(ERROR_LOGGER_T& logger) + { + logger << "\n"; + logger.flush(); + return logger; + } +} + +// Internal to this file, PTS is stored in one of these. other code uses int64_t directly. +typedef int64_t PTS_T; + +struct PACKET_DATA_T +{ + VC_CONTAINER_PACKET_T info; // copy of the API's data + std::vector buffer; // copy of the data contained, or empty if we have exceeded configuration.mem_max + uint32_t crc; // CRC32 of the data. + + // Constructor. Zeroes the whole content. + PACKET_DATA_T(): /* info((VC_CONTAINER_PACKET_T){0}), */ buffer(), crc(0) + { + // The syntax above for initialising info is probably correct according to C++ 0x. + // Unfortunately the MS C++ compiler in VS2012 isn't really C++0x compliant. + memset(&info, 0, sizeof(info)); + } +}; + +typedef uint32_t STREAM_T; + +// Packets in a file in an arbitrary order, usually the order they were read. +typedef std::list ALL_PACKETS_T; + +// Packets keyed by time (excludes packets with no PTS). This collection must be on a per-stream basis. +typedef std::map TIMED_PACKETS_T; + +// Packets with times. Must be split per-stream as multiple streams may have the same PTS. +// (It's actually unusual for more than one stream to have key frames) +typedef std::map STREAM_TIMED_PACKETS_T; + +// structure parsing and holding configuration information for the program. +struct CONFIGURATION_T +{ + // maximum size of in-memory buffers holding file data. Set by -m + size_t mem_max; + + // The source file or URL being processed + std::string source_name; + + // trace verbosity, to reflect the older test application. Set by -v. + int32_t verbosity, verbosity_input, verbosity_output; + + // fatal errors flag set by -k + bool errors_not_fatal; + + // dump-path for packet summary set by -d. May be std::cout. + mutable std::ostream* dump_packets; + + // tolerance values set by -t. + // Ideally when we seek to a time all the tracks would be right on it, but that's not always possible. + // When we don't have seek_forwards set: + // - The video must not be after the requested time + // - The video must not be more than + PTS_T tolerance_video_early; + // microseconds before the desired time. A 'good' container will hit it bang on if the supplied time is a video key frame. + PTS_T tolerance_video_late; + + // - The other tracks must not be more than + PTS_T tolerance_other_early; + PTS_T tolerance_other_late; + // from the time of the video frame. If forcing is not supported we have to take what's in the file. + + // If set by -p a test is performed re-reading the file using a packet buffer smaller than the expected packet: + // If <1 it's a proportion (e.g 0.5 will read half the packet, then the other half on a subsequent read) + // If 1 or more it's a size (1 will read 1 byte at a time; 100 not more than 100 bytes in each read) + // Defaults to -ve value, which means don't do this test. + double packet_buffer_size; + + // Constructor + CONFIGURATION_T(int argc, char** argv) + : mem_max(0x40000000) // 1Gb for 32-bit system friendliness. + , verbosity(0) // no trace + , verbosity_input(0) + , verbosity_output(0) + , errors_not_fatal(false) + , dump_packets(nullptr) + , tolerance_video_early(100000) // 100k uS = 100mS + , tolerance_video_late(0) + , tolerance_other_early(100000) // 100k uS = 100mS + , tolerance_other_late(1000000) // 1000k uS = 1S + , packet_buffer_size(-1) + { + if (argc < 2) + { + std::cout << + "-d produce packet dump (to file if specified)" << std::endl << + "-k keep going on errors" << std::endl << + "-m max memory buffer for packet data (default 1Gb). If the file is large not all will be validated properly." << std::endl << + "-p re-read using a small packet buffer. If >= 1 this is a size; if < 1 a proportion of the packet" << std::endl << + "-v verbose mode." << std::endl << + " -vi input verbosity only" << std::endl << + " -vo output verbosity only" << std::endl << + " add more vvv to make it more verbose" << std::endl << + "-t seek error tolerance in microseconds" << std::endl << + " tv video streams" << std::endl << + " to other streams" << std::endl << + " te, tl all streams earliness, lateness" << std::endl << + " tvl, tol video, other lateness" << std::endl << + " toe, tve video, other earliness" << std::endl << std::endl << + + "example: autotest -k 1-128.wmv -vvvvv -t1000000" << std::endl << + " tests 1-128.wmv. Keeps going on errors. Very verbose. Tolerant of errors up to 1s in seeks." << std::endl; + exit(0); + } + // Parse each argument + for (int arg = 1; arg > mem_max; + if (!number.eof()) + { + error(arg, argstr, "Size cannot be parsed"); + } + } + break; + + case 'p': + { + std::istringstream number(argstr); + + // throw away the p + number.ignore(1); + + if (number.eof()) + { + error(arg, argstr, "Packet re-read size not supplied"); + } + + // read the number + number >> packet_buffer_size; + if (!number.eof()) + { + error(arg, argstr, "Size cannot be parsed"); + } + } + break; + + case 't': + // error tolerance + { + std::istringstream stream(argstr); + + stream.ignore(1); // throw away the t + + // flags for which tolerance we are processing. + bool video = true, other = true, earliness=true, lateness = true; + int64_t tolerance; + + // If there's a v or an o it's video or other only + if (!stream.eof()) + { + switch (stream.peek()) + { + case 'v': + other = false; // if he said video we don't touch the other params + stream.ignore(1); // throw away the letter + break; + + case 'o': + video = false; + stream.ignore(1); // throw away the letter + break; + + // do nothing on other chars. + } + } + + // If there's an l or an e it's late or early only + if (!stream.eof()) + { + switch (stream.peek()) + { + case 'l': + earliness = false; + stream.ignore(1); // throw away the letter + break; + + case 'e': + lateness = false; + stream.ignore(1); // throw away the letter + break; + + // do nothing on other chars. + } + } + + // read the number that follows + if (stream.eof()) + { + error(arg, argstr, "tolerance not supplied"); + } + else + { + // read the number + stream >> tolerance; + if (!stream.eof()) + { + error(arg, argstr, "Number cannot be parsed"); + } + + if (video && earliness) + { + tolerance_video_early = tolerance; + } + if (video && lateness) + { + tolerance_video_late = tolerance; + } + if (other && earliness) + { + tolerance_other_early = tolerance; + } + if (other && lateness) + { + tolerance_other_late = tolerance; + } + } + } + break; + + // verbosity. v, vi or vo followed by zero or more extra v. + case 'v': + process_vees(arg, argstr); + break; + + // anything else + default: + error(arg, argstr, "is not understood"); + } + } + } + + if (source_name.empty()) + { + std::cerr << "No source name supplied"; + exit(VC_CONTAINER_ERROR_URI_NOT_FOUND); + } + + if (verbosity != 0) + { + if (verbosity_input == 0) + { + verbosity_input = verbosity; + } + + if (verbosity_output == 0) + { + verbosity_output = verbosity; + } + } + + std::cout << "Source: " << source_name << std::endl; + std::cout << "Max buffer size: " << mem_max << " 0x" << std::hex << mem_max << std::dec << std::endl; + std::cout << "Verbosity: " << verbosity << std::endl; + std::cout << "Input verbosity: " << verbosity_input << std::endl; + std::cout << "Output verbosity: " << verbosity_output << std::endl; + std::cout << "Continue on errors: " << (errors_not_fatal ? 'Y' : 'N') << std::endl; + std::cout << "Seek tolerance (uS)" << std::endl; + std::cout << " Video Early: " << tolerance_video_early << std::endl; + std::cout << " Video Late: " << tolerance_video_late << std::endl; + std::cout << " Other Early: " << tolerance_other_early << std::endl; + std::cout << " Other Late: " << tolerance_other_late << std::endl; + std::cout << "Dump Summary: " << (dump_packets ? 'Y' : 'N') << std::endl; + } +private: + + // processing for -v parameter. + void process_vees(int arg, const std::string& argstr) + { + std::istringstream vees(argstr); + + // we know we have v, so we can drop it. + vees.ignore(1); + + int32_t* which_param = &verbosity; + int32_t value = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; + + // process the rest of the characters, if any + switch (vees.peek()) + { + case 'v': + // do nothing yet + break; + + case 'i': + which_param = &verbosity_input; + vees.ignore(1); + break; + + case 'o': + which_param = &verbosity_output; + vees.ignore(1); + break; + + default: + if (vees.peek() != std::char_traits::eof()) + { + error(arg, argstr, "verbosity is not understood"); + } + break; + } + + while (1) + { + int next = vees.get(); + if (next == 'v') + { + // add verbosity + value = (value << 1) | 1; + } + else if (next == std::char_traits::eof()) + { + break; + } + else + { + error(arg, argstr, "verbosity is not understood"); + } + } + + // store what we parsed. + *which_param = value; + } + + // error handling function. Does not return. + void error(int arg, const std::string& argstr, const std::string& msg) + { + std::cerr << "Argument " << arg << " \"" << argstr << "\" " << msg; + exit(VC_CONTAINER_ERROR_INVALID_ARGUMENT); + } +}; + +// Most of the functionality of the program is in this class, or its subsidiaries. +class TESTER_T +{ +public: + // Construct, passing command line parameters + TESTER_T(int argc, char**argv) + : configuration(argc, argv) + , video_stream(std::numeric_limits::max()) + { + // If the configuration said keep going on errors tell the logger. + if (configuration.errors_not_fatal) + { + error_logger.set_errors_not_fatal(); + } + + // Tell it where any -d routed the file summary + error_logger.set_output(configuration.dump_packets); + } + + // Run the tests + VC_CONTAINER_STATUS_T run() + { + /* Set the verbosity */ + vc_container_log_set_verbosity(0, configuration.verbosity_input); + + // Open the container + p_ctx = vc_container_open_reader(configuration.source_name.c_str(), &status, 0, 0); + + if(!p_ctx || (status != VC_CONTAINER_SUCCESS)) + { + error_logger << "error opening file " << configuration.source_name << " Code " << status << std::ends; + } + + // Find the video stream. + for(size_t i = 0; i < p_ctx->tracks_num; i++) + { + if (p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) + { + if (video_stream == std::numeric_limits::max()) + { + video_stream = i; + } + else + { + // we've found two video streams. This is not necessarily an error, but it's certainly odd - perhaps it's the angles + // from a DVD. We don't expect to see it, but report it anyway. Don't stop, just assume that the first one is the one we want. + error_logger << "Both track " << video_stream << " and " << i << " are marked as video streams" << std::ends; + } + } + } + + if (video_stream == std::numeric_limits::max()) + { + error_logger << "No video track found" << std::ends; + } + + // Read all the packets sequentially. This will gve us all the metadata of the packets, + // and (up to configuration.mem_max) will also store the packet data. + read_sequential(); + + // Now we have some packets we can initialise our internal RNG. + init_crg(); + + // Find all the keyframes in the collection we just read. + find_key_packets(); + + // Seeking tests. Only if the container supports it. + // We do this first, because one of the checks is whether or not the container supports seeking, + // and we want to be able to seek back between tests. + if (p_ctx->capabilities & VC_CONTAINER_CAPS_CAN_SEEK) + { + /// Seek to PTS 0 (this ought to be the beginning) + PTS_T beginning = 0; + status = vc_container_seek(p_ctx, &beginning, VC_CONTAINER_SEEK_MODE_TIME, 0); + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Container failed to seek to the beginning - status " << status << std::ends; + + // This is fatal. + exit(status); + } + + // Ask for info about the first packet + PACKET_DATA_T actual; + status = vc_container_read(p_ctx, &actual.info, VC_CONTAINER_READ_FLAG_INFO); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Read info failed after seek to the beginning - status " << status << std::ends; + } + else + { + PACKET_DATA_T& expected = all_packets.front(); + // Compare some selected data to check that this genuinely is the first packet. + // We don't actually do a read. + if ((expected.info.pts != actual.info.pts) + || (expected.info.dts != actual.info.dts) + || (expected.info.size != actual.info.size) + || (expected.info.frame_size != actual.info.frame_size) + || (expected.info.track != actual.info.track) + || (expected.info.flags != actual.info.flags)) + { + // Copy the CRC from the expected value. That will stop compare_packets whinging. + actual.crc = expected.crc; + + // report the discrepancy + error_logger << "Seek to time zero did not arrive at beginning: " + << compare_packets(expected, actual) << std::ends; + } + } + + // Perform seeks to find all the packets that a seek will go to. + check_indices(0); + check_indices(VC_CONTAINER_SEEK_FLAG_FORWARD); + + // If it supports forcing then seek to each of those locations, and force read all the tracks. + if (p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) + { + check_seek_then_force(0); + check_seek_then_force(VC_CONTAINER_SEEK_FLAG_FORWARD); + } + + seek_randomly(0); + seek_randomly(VC_CONTAINER_SEEK_FLAG_FORWARD); + + // todo more? + } + else + { + // The file claims not to support seeking. Perform a seek, just to check this out. + PTS_T beginning = 0; + + status = vc_container_seek(p_ctx, &beginning, VC_CONTAINER_SEEK_MODE_TIME, 0); + + if (status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION) + { + error_logger << "Container did not reject seek when its capabilities said it is not supported" << std::ends; + } + } + + // If the config didn't ask for this test don't do it. + if (configuration.packet_buffer_size > 0) + { + re_seek_to_beginning(); + + // Read through the file, reading the packet in several bites (bite size controlled by -p parameter) + check_partial_reads(); + + // Check forcing (this doesn't require seek) + if (p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) + { + re_seek_to_beginning(); + check_forcing(); + } + } + + std::cout << "Test Complete" << std::endl; + + // If anything failed then the error logger will replace this error code. So always return 0 from here. + return VC_CONTAINER_SUCCESS; + } +private: + // read packets from the file, and stash them away. + void read_sequential() + { + // Use the same buffer for reading each of the packets. It's cheaper to copy the (usually small) data + // from this buffer than to create a new one for each packet, then resize it. + // 256k is the size used in the other program, and is bigger than any known packet. Increase it + // if a bigger one is found. + std::vector buffer(256*1024, 0); + + std::map packet_counts; + + // Count of memory used for packet buffers. + size_t memory_buffered = 0; + + if (configuration.dump_packets) + { + *configuration.dump_packets << "pts" << ",\t" << "track" << ",\t" << "size" << ",\t" << "crc" << std::endl; + } + + while (1) // We break out at EOF. + { + // Packet object + PACKET_DATA_T packet; + + // Use the shared buffer for the moment, + packet.info.data = &buffer[0]; + packet.info.buffer_size = buffer.size(); + + // Read data. + status = vc_container_read(p_ctx, &packet.info, 0); + + if (packet.info.size >= buffer.size()) + { + std::cerr << "Packet size limit exceeeded. Increase internal buffer size!"; + exit(VC_CONTAINER_ERROR_FAILED); + } + + if (status == VC_CONTAINER_SUCCESS) + { + // Calculate the CRC of the packet + packet.crc = crc32buf(&buffer[0], packet.info.size); + + // If there is any data, and we haven't exceeded our size limit... + if ((packet.info.size > 0) && (memory_buffered < configuration.mem_max)) + { + // Copy the data we read across from the big buffer to our local one + packet.buffer.assign(buffer.begin(), buffer.begin() + packet.info.size); + + // count how much we have buffered + memory_buffered += packet.info.size; + + // wipe the bit of the big buffer we used + memset(&buffer[0], 0, packet.info.size); + } + else + { + // not storing any data, either because there is none or we have no space. + packet.buffer.clear(); + } + + // Clear the pointer in our data. It won't be valid later. + packet.info.data = 0; + + // Set the size of the buffer to match what is really there. + packet.info.buffer_size = packet.info.size; + + // count the packet + ++packet_counts[packet.info.track]; + + // store it + all_packets.push_back(packet); + + // perhaps dump it + if (configuration.dump_packets) + { + if (packet.info.pts < 0) + { + *configuration.dump_packets << "-"; + } + else + { + *configuration.dump_packets << packet.info.pts; + } + + *configuration.dump_packets + << ",\t" << packet.info.track << ",\t" << packet.info.size << ",\t" << packet.crc << std::endl; + } + } + else if (status == VC_CONTAINER_ERROR_EOS) + { + break; + } + else + { + error_logger << "error reading file " << configuration.source_name << " Code " << status << std::ends; + } + } + + std::cout << std::dec << "File has " << packet_counts.size() << " tracks." << std::endl; + + // Print the packet count for each stream. Also, while iterating, save all the stream numbers. + for(std::map::const_iterator stream = packet_counts.begin(); stream != packet_counts.end(); ++stream) + { + // Print the packet count + std::cout << "Stream " << stream->first << " has " << stream->second << " packets." << std::endl; + + // Note that we have a stream with this number. + all_streams.insert(stream->first); + }; + + if (p_ctx->tracks_num != packet_counts.size()) + { + error_logger << "The file header claims " << p_ctx->tracks_num + << " but " << packet_counts.size() << " streams were found" << std::ends; + } + } + + // Search the all_packets collection for key frames, and store them in all_key_packets + void find_key_packets() + { + // The last known PTS for each stream. + std::map last_pts_in_stream; + + for (ALL_PACKETS_T::const_iterator packet = all_packets.begin(); packet != all_packets.end(); ++packet) + { + PTS_T pts = packet->info.pts; + STREAM_T stream = packet->info.track; + + // If it has a PTS check they are an ascending sequence. + if (pts >= 0) + { + // Find the last PTS, if any, for this stream + std::map::const_iterator last_known_pts = last_pts_in_stream.find(stream); + + if ((last_known_pts != last_pts_in_stream.end()) && (pts <= last_known_pts->second)) + { + // the PTS isn't bigger than the previous best. This is an error. + error_logger << "Out of sequence PTS " << pts << " found. Previous largest was " + << last_pts_in_stream[stream] << std::ends; + } + + // store it (even if bad) + last_pts_in_stream[stream] = pts; + + // Store in the collection of packets with PTSes + all_pts_packets[stream].insert(std::make_pair(pts, packet)); + + // if it is also a keyframe + if (packet->info.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) + { + // Put it into the collection for its track. + all_key_packets[stream].insert(std::make_pair(pts, packet)); + } + } + } + } + + // Check which locations can be accessed by a seek, with or without VC_CONTAINER_SEEK_FLAG_FORWARD. + void check_indices(VC_CONTAINER_SEEK_FLAGS_T direction) + { + STREAM_TIMED_PACKETS_T& index_positions = direction == 0 ? reverse_index_positions : forward_index_positions; + // Go through each stream that contains key packets (usually only one) + for (STREAM_TIMED_PACKETS_T::const_iterator stream_keys = all_key_packets.begin(); + stream_keys != all_key_packets.end(); + ++stream_keys) + { + // Start with a PTS just after the last key packet, and repeat until we fall off the beginning. + for (PTS_T target_pts = stream_keys->second.rbegin()->first + 1; target_pts >= 0; /* no decrement */) + { + // copy the PTS value to pass to vc_container_seek + PTS_T actual_pts = target_pts; + + // Seek to that position in the file. + status = vc_container_seek(p_ctx, &actual_pts, VC_CONTAINER_SEEK_MODE_TIME, direction); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " seeking to PTS " << target_pts << std::ends; + continue; // if errors are not fatal we'll try again 1uS earlier. + } + + // Check whether this seek reported that it went somewhere sensible + check_correct_seek(direction, actual_pts, target_pts); + + // Validate that the place it said we arrived at is correct - read info for the first packet in any stream + { + PACKET_DATA_T packet; + status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_INFO); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " reading info for packet at PTS " << target_pts << std::ends; + continue; // stop trying to read at this PTS. + } + + if (packet.info.pts != actual_pts) + { + error_logger << "Incorrect seek. Container claimed to have arrived at " + << actual_pts << " but first packet "; + + if (packet.info.pts < 0) + { + error_logger << " had no PTS"; + } + else + { + error_logger << "was at " << packet.info.pts; + } + + error_logger << std::ends; + } + } + + // We'll perform reads until we've had a packet with PTS from every stream. + for (std::set unfound_streams = all_streams; + !unfound_streams.empty(); + /* unfound_streams.erase(packet.info.track) */ ) + { + // Read a packet. We can't be sure what track it will be from, so first read the info... + PACKET_DATA_T packet; + status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_INFO); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " reading info for packet following PTS " << target_pts << std::ends; + break; // stop trying to read at this PTS. + } + + // allocate a buffer. + packet.buffer.resize(packet.info.size); + packet.info.data = &packet.buffer[0]; + packet.info.buffer_size = packet.info.size; + + // perform the read + status = vc_container_read(p_ctx, &packet.info, 0); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " reading packet following PTS " << target_pts << std::ends; + break; // stop trying to read at this PTS. + } + + STREAM_T stream = packet.info.track; + + // If it has no PTS we can't use it. Read another one. + if (packet.info.pts < 0) + { + // The first packet we found for a stream has no PTS. + // That's odd, because we wouldn't be able to play it. However, + // if the stream doesn't permit forcing it may be inevitable. + if ((unfound_streams.find(stream) != unfound_streams.end()) + && ((p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) != 0)) + { + error_logger << "Packet in stream " << stream + << " has no PTS after seeking to PTS " << target_pts << std::ends; + } + continue; + } + + // Search for the PTS collection for this stream + STREAM_TIMED_PACKETS_T::iterator stream_packets = all_pts_packets.find(stream); + + if (stream_packets == all_pts_packets.end()) + { + error_logger << "Packet from unknown stream " << stream + << "found when reading packet at PTS " << target_pts << std::ends; + continue; // try reading another packet. + } + + // Look for the packet for this PTS in this stream. + TIMED_PACKETS_T::const_iterator expected_packet = stream_packets->second.find(packet.info.pts); + + if (expected_packet == stream_packets->second.end()) + { + error_logger << "Read packet from stream " << stream << " has unknown PTS " << packet.info.pts << std::ends; + continue; + } + + // calculate the CRC + packet.crc = crc32buf(&packet.buffer[0], packet.info.size); + + // Validate that the data is the data we found when we did the sequential reads. + std::string anyerror = compare_packets(*expected_packet->second, packet); + + if (!anyerror.empty()) + { + error_logger << "Incorrect data found at PTS " << actual_pts << anyerror << std::ends; + } + + // If this is the first packet we found for this stream + if (unfound_streams.find(stream) != unfound_streams.end()) + { + // Store the packet we found. Note we key it by the PTS we used for the seek, + // not its own PTS. This should mean next time we seek to that PTS we'll get this packet. + index_positions[stream].insert(std::make_pair(target_pts, expected_packet->second)); + + // Check whether the time on the packet is reasonable - it ought to be close to the time achieved. + check_seek_tolerance(packet, actual_pts); + + // Now we've found a packet from this stream we're done with it, so note we don't care about it any more. + unfound_streams.erase(stream); + } + } // repeat until we've had a packet for every track + + // Adjust the target location + if (actual_pts > target_pts) + { + // Find the next packet down from where we looked, and try there. + TIMED_PACKETS_T this_stream_keys = stream_keys->second; + TIMED_PACKETS_T::const_iterator next_packet = this_stream_keys.lower_bound(target_pts); + + // If there is such a packet that it has more than this PTS, and it isn't the first packet + if ((next_packet != this_stream_keys.begin()) && + (next_packet != this_stream_keys.end())) + { + // pull out the PTS from the one before, and ask for a microsecond past it. + PTS_T new_target_pts = (--next_packet)->first + 1; + if (new_target_pts >= target_pts) + { + --target_pts; + } + else + { + target_pts = new_target_pts; + } + } + else + { + // There's no packet earlier than where we are looking. + // First time try 1 next time try zero. + target_pts = target_pts != 1 ? 1 : 0; + } + } + else if (actual_pts < (target_pts - 1)) + { + // The place we arrived at is well before where we wanted. + // Next time go just after where we arrived. + target_pts = actual_pts + 1; + } + else + { + // If the place we asked for is where we arrived, next time try one microsecond down. + --target_pts; + } + } + } + } + + // Seek to all feasible locations and perform force reads on all possible tracks + void check_seek_then_force(VC_CONTAINER_SEEK_FLAGS_T direction) + { + // Depending on whether we are doing forward or reverse seeks pick a collection of places a seek can go to. + STREAM_TIMED_PACKETS_T index_positions = direction == 0 ? reverse_index_positions : forward_index_positions; + + // Go through all the streams + for (STREAM_TIMED_PACKETS_T::const_iterator index_stream = index_positions.begin(); + index_stream != index_positions.end(); + ++index_stream) + { + // Go through all the packets in each stream that can be indexed + for (TIMED_PACKETS_T::const_iterator location = index_stream->second.begin(); + location != index_stream->second.end(); + ++location + ) + { + // This is the time we expect + PTS_T actual_pts = location->first; + + // Seek to that position in the file. + status = vc_container_seek(p_ctx, &actual_pts, VC_CONTAINER_SEEK_MODE_TIME, direction); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " seeking to PTS " << location->first << std::ends; + continue; // if errors are not fatal we'll try the next position. + } + + // We'll perform force-reads until we've had a packet from every stream that has any PTS in it. + for (STREAM_TIMED_PACKETS_T::const_iterator stream = all_pts_packets.begin(); stream != all_pts_packets.end(); ++stream) + { + // Read a packet. + PACKET_DATA_T packet; + packet.info.track = stream->first; + + // This loop repeats occasionally with a continue, but usually stops on the break at the end first time around. + while(true) + { + status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_FORCE_TRACK); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " force-reading info for stream " + << location->first << " after seeking to PTS " << stream->first << std::ends; + + // If we can't read the info there's no point in anything else on this stream + break; + } + + // allocate a buffer. + packet.buffer.resize(packet.info.size); + packet.info.data = &packet.buffer[0]; + packet.info.buffer_size = packet.info.size; + + // perform the actual read + status = vc_container_read(p_ctx, &packet.info, VC_CONTAINER_READ_FLAG_FORCE_TRACK); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " force-reading stream " + << location->first << " after seeking to PTS " << stream->first << std::ends; + + // If we didn't read successfully we can't check the data. Try the next stream. + break; + } + + // If it has no PTS we can't use it - and it can't be played either. + if (packet.info.pts < 0) + { + error_logger << "Packet force-read on stream " << stream->first + << " has no PTS after seeking to PTS " << location->first << std::ends; + + // Try force-reading another. + continue; + } + + // Make sure that the packet is near the time we wanted. + check_seek_tolerance(packet, actual_pts); + + // Look for the packet for this PTS in this stream. + TIMED_PACKETS_T::const_iterator expected_packet = stream->second.find(packet.info.pts); + + if (expected_packet == stream->second.end()) + { + error_logger << "Packet force-read on stream " << stream->first + << " has unknown PTS " << packet.info.pts << std::ends; + + // Try force-reading another. + continue; + } + + packet.crc = crc32buf(packet.info.data, packet.info.size); + + // Validate that the data is the data we found when we did the sequential reads. + std::string anyerror = compare_packets(*expected_packet->second, packet); + + if (!anyerror.empty()) + { + error_logger << "Incorrect data found at PTS " << actual_pts << anyerror << std::ends; + } + + // If we arrive here we're done for this stream. + break; + } + } + // repeat until we've had a packet for every stream at this index position + } + // repeat for every index position in the stream + } + // repeat for every stream that has index positions + } + + // seek to a random selection of places, and see if we get the correct data + void seek_randomly(VC_CONTAINER_SEEK_FLAGS_T direction) + { + // Depending on whether we are doing forward or reverse seeks pick a collection of places a seek can go to. + const STREAM_TIMED_PACKETS_T& index_positions = direction == 0 ? reverse_index_positions : forward_index_positions; + + // Go through each stream in that collection + for (STREAM_TIMED_PACKETS_T::const_iterator i_all_index_packets = index_positions.begin(); + i_all_index_packets != index_positions.end(); + ++i_all_index_packets) + { + // These are the index packets in the stream. + const TIMED_PACKETS_T& all_index_packets = i_all_index_packets->second; + + // this is the number of seeks we'll perform. + size_t seek_count = all_index_packets.size(); + + // If there are more than 100 locations limit it to 100. + if (seek_count > 100) + { + seek_count = 100; + } + + // We want an unsorted list of PTSes. + std::list selected_index_packets; + { + // Picking 100 at random out of a potentially large collection of timestamps in a set is going + // to be an expensive operation - search by key is really fast, but search by position is not. + // This is an attempt to come up with something reasonably efficient even if the collection is large. + std::vector all_ptses; + + // Reserve enough memory to hold every PTS. This collection should be several orders of magnitude smaller than the file. + all_ptses.reserve(all_index_packets.size()); + + // Copy all the PTS values into the vector + std::transform( + all_index_packets.begin(), + all_index_packets.end(), + std::back_inserter(all_ptses), + [](const TIMED_PACKETS_T::value_type& source){ return source.first; } + ); + + // Based roughly on the Knuth shuffle, get a random subset of the PTS to the front of the collection. + std::vector::iterator some_pts; + size_t count; + for (count = 0, some_pts = all_ptses.begin(); count < seek_count; ++count, ++some_pts) + { + // Swap some random PTS into one of the first seek_count locations. + // Note this may well be another of the first seek_count locations, especially if this is most of them + std::iter_swap(some_pts, (all_ptses.begin() + rand() % seek_count)); + } + + // Throw away the ones we don't want + all_ptses.resize(seek_count); + + // Copy the iterators for each packet with a selected PTS into the list. + std::transform( + all_ptses.begin(), + all_ptses.end(), + std::back_inserter(selected_index_packets), + [&all_index_packets](PTS_T time){ return *all_index_packets.find(time); }); + + // End scope. That will free the rest of all_ptses. + } + + // Loop through the selected packets. + for (std::list::iterator expected = selected_index_packets.begin(); + expected != selected_index_packets.end(); + ++expected) + { + // Seek to their position & check we got there + const PACKET_DATA_T& target = *expected->second; + PTS_T target_pts = expected->first; + + status = vc_container_seek(p_ctx, &target_pts, VC_CONTAINER_SEEK_MODE_TIME, direction); + check_correct_seek(direction, target_pts, expected->first); + + // Start by initialising the new packet object to match the old one - it's mostly right. + PACKET_DATA_T found_packet = target; + + // If forcing is supported, read the first packet & check it's OK. + if (p_ctx->capabilities & VC_CONTAINER_CAPS_FORCE_TRACK) + { + // There might not be a buffer if we ran out of memory + found_packet.buffer.resize(found_packet.info.size); + + // Set the address + found_packet.info.data = &found_packet.buffer[0]; + + // Perform the read + status = vc_container_read(p_ctx, &found_packet.info, VC_CONTAINER_READ_FLAG_FORCE_TRACK); + } + else + { + // as forcing is not supported repeat reads until a packet is found for this track. + do + { + status = vc_container_read(p_ctx, &found_packet.info, VC_CONTAINER_READ_FLAG_INFO); + + // Give up on any error + if (status != VC_CONTAINER_SUCCESS) break; + + // Set the buffer to match the actual packet + found_packet.info.buffer_size = found_packet.info.size; + found_packet.buffer.resize(found_packet.info.size); + found_packet.info.data = &found_packet.buffer[0]; + status = vc_container_read(p_ctx, &found_packet.info, 0); + + if (status != VC_CONTAINER_SUCCESS) break; + } + while (found_packet.info.track != target.info.track); + } + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " after random seek to PTS " << target_pts << std::ends; + } + else + { + found_packet.crc = crc32buf(&found_packet.buffer[0], found_packet.info.size); + + // We now have a packet from the right stream - it ought to be the one we asked for. + const std::string& compare_result = compare_packets(found_packet, target); + + if (!compare_result.empty()) + { + error_logger << "Incorrect result from reading packet after random seek: " << compare_result << std::ends; + } + } + } + } + } + + // Seek back to the beginning of the file, either with a seek or by closing and re-opening it + void re_seek_to_beginning() + { + // If the container supports seek do it. + if (p_ctx->capabilities & VC_CONTAINER_CAPS_CAN_SEEK) + { + PTS_T beginning = 0; + + status = vc_container_seek(p_ctx, &beginning, VC_CONTAINER_SEEK_MODE_TIME, 0); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Failed to seek back to the beginning" << std::ends; + } + } + else + { + // The file claims not to support seeking. Close it, and re-open - this should do it. + status = vc_container_close(p_ctx); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " Failed to close the container." << std::ends; + } + + p_ctx = vc_container_open_reader(configuration.source_name.c_str(), &status, 0, 0); + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " Failed to re-open the container." << std::ends; + + // Even with -k this is a fatal error. Give up. + exit(status); + } + } + } + + // Read through the file, checking to see that the packets we get match the ones we read first time around - + // given that we're going to read with a buffer of a different size, either a fixed value or a proportion + // of the actual packet size. + void check_partial_reads() + { + // This is used for some reads, and for compares. + PACKET_DATA_T actual; + + // Repeat until we meet EOF, which will be in the middle of the loop somewhere. + ALL_PACKETS_T::const_iterator expected; + for (expected = all_packets.begin(); expected != all_packets.end(); ++expected) + { + // Ask for info about the first packet + status = vc_container_read(p_ctx, &actual.info, VC_CONTAINER_READ_FLAG_INFO); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " reading info for partial packet" << std::ends; + // Not much point in carrying on if we can't read - but usually this is the end-of-file break. + break; + } + + size_t whole_packet = actual.info.size; + + // Work out how big a read we want to do. + size_t wanted_read_size = (configuration.packet_buffer_size >= 1.0) + ? (size_t)configuration.packet_buffer_size + : (size_t)(configuration.packet_buffer_size * (double)whole_packet); + + // Make sure it's at least 1 byte (a small proportion of a small packet might round down to zero) + if (wanted_read_size == 0) + { + wanted_read_size = 1; + } + + // Ensure our buffer is at least big enough to contain the _whole_ packet. + if (whole_packet > actual.buffer.size()) + { + actual.buffer.resize(whole_packet); + } + + // We'll need to collect some data from the bits of the packet as they go by. + PTS_T first_valid_pts = std::numeric_limits::min(); + PTS_T first_valid_dts = std::numeric_limits::min(); + uint32_t total_flags = 0; + + size_t amount_read; + + // Loop around several times, reading part of the packet each time + for (amount_read = 0; amount_read < whole_packet; /* increment elsewhere */) + { + // Somewhere in the buffer + actual.info.data = &actual.buffer[amount_read]; + + // Not more than our calculated size + actual.info.buffer_size = wanted_read_size; + + // read some data. + status = vc_container_read(p_ctx, &actual.info, 0); + + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Unable to read " << wanted_read_size << " bytes from packet of size " + << expected->info.size << " error " << status << std::ends; + + // Guess we're at end of packet. We _might_ be able to recover from this. + break; + } + + amount_read += actual.info.size; + + // validate the amount read is less than the request + if (actual.info.size > wanted_read_size) + { + error_logger << "Too much data read from packet. Request size was " << whole_packet + << " but " << actual.info.size << "read in" << std::ends; + } + + // and that the total amount read for this packet is not too much + if (amount_read > whole_packet) + { + error_logger << "Too much data read from packet. Total size is " << whole_packet + << " but " << amount_read << "read in" << std::ends; + } + + // OR all the flags together + total_flags |= actual.info.flags; + + // Save the PTS if we don't have one yet + if (first_valid_pts < 0) + { + first_valid_pts = actual.info.pts; + } + + // Ditto the DTS + if (first_valid_dts < 0) + { + first_valid_dts = actual.info.dts; + } + } + + // The buffer should now contain all the correct data. However, the size field + // reflects the last read only - so correct it before the compare. + actual.info.size = amount_read; + + // store back the other stuff we got in the loop + actual.info.flags = total_flags; + actual.info.pts = first_valid_pts; + actual.info.dts = first_valid_dts; + + // Calculate the CRC of the whole lot of data + actual.crc = crc32buf(&actual.buffer[0], amount_read); + + // It's possible that the packet read isn't the one we expected. + // (This happens with the Supremecy3_20sec_WMV_MainProfile_VGA@29.97fps_2mbps_WMA9.2.wmv sample, + // where the first packet has no PTS and is not a key frame - seek won't go there) + if ((expected->info.pts != actual.info.pts) + && (actual.info.pts >= 0)) + { + ALL_PACKETS_T::const_iterator candidate = std::find_if + (all_packets.begin(), + all_packets.end(), + [&actual](ALL_PACKETS_T::value_type& expected) -> bool + { + // If we find one with the correct track and PTS, that will do. + return (expected.info.pts == actual.info.pts) + && (expected.info.track == actual.info.track); + }); + + if (candidate != all_packets.end()) + { + // We've seen the packet before + error_logger << "Partial read returned an unexpected packet. Expect PTS " << expected->info.pts + << " but got PTS " << actual.info.pts << std::ends; + + // switch to expecting this packet + expected = candidate; + } + + // If we didn't find the packet anywhere in the expected list do nothing, and let the compare fail. + } + + const std::string& compare_result = compare_packets(*expected, actual); + + if (!compare_result.empty()) + { + error_logger << "Incorrect result from reading packet in parts: " << compare_result << std::ends; + } + } + + // check end reached if we were OK this far + if (status == VC_CONTAINER_SUCCESS) + { + // We should be at the end of the expected collection + if (expected != all_packets.end()) + { + error_logger << "Failed to read entire file when reading partial packets" << std::ends; + } + + // We should not be able to read any more. + status = vc_container_read(p_ctx, &actual.info, VC_CONTAINER_READ_FLAG_INFO); + + if (status == VC_CONTAINER_ERROR_EOS) + { + status = VC_CONTAINER_SUCCESS; + } + else + { + error_logger << "Should have reached end of stream, but got status " + << status << " while reading partial packets." << std::ends; + } + } + } + + // Information used in the next function about where we are in each stream. + struct FORCING_INFO : public PACKET_DATA_T + { + void new_packet() + { + first_valid_pts = std::numeric_limits::min(); + first_valid_dts = std::numeric_limits::min(); + total_flags = 0; + + // Adjust the buffer to hold the amount we want. + // (note - resize smaller does not re-allocate, + // it'll grow until it reaches the biggest packet then stay at that size) + buffer.resize(info.buffer_size); + + info.data = &buffer[0]; + } + + PTS_T first_valid_pts; + PTS_T first_valid_dts; + uint32_t total_flags; + ALL_PACKETS_T::const_iterator position; + }; + + // Check forcing. Read bits of the streams so that they get out of sync, and make sure the data is correct. + void check_forcing() + { + // Data for each stream + std::map stream_positions; + std::map::iterator stream_pos; + + // Set stream_positions to the first packet for each stream + for (std::set::const_iterator stream = all_streams.begin(); + stream != all_streams.end(); + ++stream) + { + const STREAM_T stream_number = *stream; + FORCING_INFO stream_data; + stream_data.position = all_packets.begin(); + + // Insert an entry for this stream, starting at the beginning + stream_pos = stream_positions.insert(std::make_pair(stream_number, stream_data)).first; + + // Then search for a packet in this stream. + next_in_stream(stream_pos); + + stream_pos->second.info.track = stream_number; + } + + // Repeat until explicit break when all have reached last packet + bool alldone = false; + while (!alldone) + { + // go through each stream in turn and get some data. At end of packet check it. + for (stream_pos = stream_positions.begin(); stream_pos != stream_positions.end(); ++stream_pos) + { + FORCING_INFO& stream_data = stream_pos->second; + if (stream_data.position == all_packets.end()) + { + // This stream has reached the end of the expected packets. Force-read an info, + // to make sure it really is at the end. + status = vc_container_read(p_ctx, &stream_data.info, + VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_FORCE_TRACK); + + if (status != VC_CONTAINER_ERROR_EOS) + { + error_logger << "Reading from stream " << stream_pos->first + << " after end gave error " << status << "instead of EOS" << std::ends; + + // Erase the buffer so we don't repeat the check. We'd just end up with loads of errors. + stream_positions.erase(stream_pos); + + // Then reset the iterator, as that made it invalid. + stream_pos = stream_positions.begin(); + } + } + else + { + // This stream has not reached the end. Read some more, maybe check it. + + // If we haven't read anything in yet + if (stream_data.info.size == 0) + { + if (configuration.packet_buffer_size <= 0) + { + // if unspecified read whole packets. + stream_data.info.buffer_size = stream_data.position->info.size; + } + else if (configuration.packet_buffer_size < 1.0) + { + // If a proportion work it out + stream_data.info.buffer_size + = (uint32_t)(configuration.packet_buffer_size * stream_data.position->info.size); + + if (stream_data.info.buffer_size < 1) + { + // but never less than 1 byte + stream_data.info.buffer_size = 1; + } + } + else + { + // Use the amount specified + stream_data.info.buffer_size = (uint32_t)configuration.packet_buffer_size; + } + + stream_data.new_packet(); + } + else + { + // We've already read part of the packet. Read some more. + size_t read_so_far = stream_data.buffer.size(); + + // expand the buffer to hold another lump of data + stream_data.buffer.resize(read_so_far + stream_data.info.buffer_size); + + // point the read at the new part. + stream_data.info.data = &stream_data.buffer[read_so_far]; + } + + // Read some data + status = vc_container_read(p_ctx, &stream_data.info, VC_CONTAINER_READ_FLAG_FORCE_TRACK); + + stream_data.total_flags |= stream_data.info.flags; + // Save the PTS if we don't have one yet + if (stream_data.first_valid_pts < 0) + { + stream_data.first_valid_pts = stream_data.info.pts; + } + + // Ditto the DTS + if (stream_data.first_valid_dts < 0) + { + stream_data.first_valid_dts = stream_data.info.dts; + } + + // work out how much we have. + uint32_t total_read = (stream_data.info.data + stream_data.info.size) - &stream_data.buffer[0]; + + if (total_read > stream_data.position->info.size) + { + // we've read more than a packet. That's an error. + error_logger << "Read more data than expected from a packet - read " << total_read + << " Expect " << stream_data.position->info.size; + } + + if (total_read < stream_data.position->info.size) + { + // more to read. Just go back around the loop. + continue; + } + + stream_data.info.size = total_read; + + stream_data.crc = crc32buf(&stream_data.buffer[0], total_read); + + stream_data.info.flags = stream_data.total_flags; + stream_data.info.pts = stream_data.first_valid_pts; + stream_data.info.dts = stream_data.first_valid_dts; + + // Validate that the data is the data we found when we did the sequential reads. + std::string anyerror = compare_packets(*stream_data.position, stream_data); + + if (!anyerror.empty()) + { + error_logger << "Incorrect data found at PTS " << stream_data.info.pts << anyerror << std::ends; + } + + // Move to the next packet + ++stream_data.position; + + // Then search until we get one for this stream, or end. + next_in_stream(stream_pos); + + // Note we've read nothing of the new packet yet. + stream_data.info.size = 0; + } + } + + // go through each stream in turn. If they have all reached the end we are complete. + for (alldone = true, stream_pos = stream_positions.begin(); alldone && stream_pos != stream_positions.end(); ++stream_pos) + { + if (stream_pos->second.position != all_packets.end()) + { + // If any stream has not reached the end we have more to do + alldone = false; + } + } + } + } + + // Helper for the above: Advance the supplied FORCING_INFO to the next packet in the stream + void next_in_stream(std::map::iterator& pos) + { + // We're searching for this stream + const STREAM_T track = pos->first; + + ALL_PACKETS_T::const_iterator& position = pos->second.position; + + // Loop until no more packets, or found one for this stream + while ((position != all_packets.end()) + && (position->info.track != track)) + { + // advance this iterator to the next packet. + ++position; + } + }; + + // Compare two packets, and return a non-empty string if they don't match + std::string compare_packets(const PACKET_DATA_T& expected, const PACKET_DATA_T& actual) + { + std::ostringstream errors; + + // Check the fields in the structure + if (expected.info.size != actual.info.size) + { + errors << "size mismatch. Expect " << expected.info.size << " actual " << actual.info.size << std::endl; + } + + if (expected.info.frame_size != actual.info.frame_size) + { + errors << "frame_size mismatch. Expect " << expected.info.frame_size << " actual " << actual.info.frame_size << std::endl; + } + + if (expected.info.pts != actual.info.pts) + { + errors << "pts mismatch. Expect " << expected.info.pts << " actual " << actual.info.pts << std::endl; + } + + if (expected.info.dts != actual.info.dts) + { + errors << "dts mismatch. Expect " << expected.info.dts << " actual " << actual.info.dts << std::endl; + } + + if (expected.info.track != actual.info.track) + { + errors << "track mismatch. Expect " << expected.info.track << " actual " << actual.info.track << std::endl; + } + + if (expected.info.flags != actual.info.flags) + { + errors << "flags mismatch. Expect " << expected.info.flags << " actual " << actual.info.flags << std::endl; + } + + // check the buffer. We won't bother if there isn't one in either - if the expected hasn't got one, + // it's probably because we hit our memory limit. If the actual hasn't, the there should be a size error. + for (size_t i = 0, err_count = 0; i < expected.buffer.size() && i < actual.buffer.size(); ++i) + { + if (expected.buffer[i] != actual.buffer[i]) + { + errors << "Data mismatch at " + << std::setw(8) << std::hex << i + << std::setw(3) << (unsigned)expected.buffer[i] + << std::setw(3)<< (unsigned)actual.buffer[i] << std::endl; + + if (++err_count > 20) + { + errors << "Too many errors to report" << std::endl; + break; + } + } + } + + if (expected.crc != actual.crc) + { + errors << "CRC mismatch. Expect " << expected.crc << " actual " << actual.crc << std::endl; + } + + // If there were errors put a new line on the front. This aids formatting. + const std::string& error_list = errors.str(); + + if (error_list.empty()) + { + // There were no errors + return error_list; + } + else + { + return std::string("\n") + error_list; + } + } + + void check_correct_seek(VC_CONTAINER_SEEK_FLAGS_T direction, PTS_T actual_pts, PTS_T target_pts) + { + if (status != VC_CONTAINER_SUCCESS) + { + error_logger << "Error " << status << " returned by seek" << std::ends; + } + if (direction) + { + if (actual_pts < target_pts) + { + // We shouldn't normally arrive at a time earlier than we requested. + // However there's a special case - when the time requested is after the last keyframe + PTS_T highest_pts_in_video = all_key_packets.at(video_stream).rbegin()->first; + + if (actual_pts < highest_pts_in_video) + { + error_logger << "Incorrect forward seek. Should not be before " + << target_pts << " but arrived at " << actual_pts << std::ends; + } + } + } + else + { + if (actual_pts > target_pts) + { + // We shouldn't normally arrive at a time later than we requested. + // However there's a special case - when the time requested is before the first keyframe in the file. + PTS_T lowest_pts_in_video = all_key_packets.at(video_stream).begin()->first; + + if (actual_pts > lowest_pts_in_video) + { + error_logger << "Incorrect reverse seek. Should not be after " + << target_pts << " but arrived at " << actual_pts << std::ends; + } + } + } + } + + // Check whether a packet is close enough to the PTS supplied. Used after a seek. + void check_seek_tolerance(const PACKET_DATA_T& packet, PTS_T actual_pts) + { + // Check whether the time on the packet is reasonable - it ought to be close to the time achieved. + if (packet.info.track == video_stream) + { + if ((packet.info.pts + configuration.tolerance_video_early) < actual_pts) + { + error_logger << "Video stream seek location is bad " << actual_pts - packet.info.pts << "uS early" << std::ends; + } + + if ((packet.info.pts - configuration.tolerance_video_late) > actual_pts) + { + // We shouldn't normally arrive at a time later than we requested. + // However there's a special case - when the time requested is before the first PTS in the file. + PTS_T lowest_pts_in_video = all_pts_packets.at(video_stream).begin()->first; + + if (actual_pts > lowest_pts_in_video) + { + error_logger << "Video stream seek location is bad " << packet.info.pts - actual_pts << "uS late" << std::ends; + } + } + } + else + { + if ((packet.info.pts + configuration.tolerance_other_early) < actual_pts) + { + error_logger << "Non-video stream seek location is bad " << actual_pts - packet.info.pts << "uS early" << std::ends; + } + + if ((packet.info.pts - configuration.tolerance_other_late) > actual_pts) + { + error_logger << "Non-video stream seek location is bad " << packet.info.pts - actual_pts << "uS late" << std::ends; + } + } + } + + // Initialise a Congruential Random Number Generator from the file contents. + // Used rather than rand() for the complete control that this offers - this app + // should behave the same regardless of the platform and word size. + void init_crg() + { + if (all_packets.empty()) + { + static const char* msg = "You must read some packets before initialising the RNG"; + assert(!msg); + std::cerr << msg; + exit(1); + } + + rng_value = 0; + + // XOR all the CRCs together to act as a file-specific seed. + for (ALL_PACKETS_T::const_iterator packet = all_packets.begin(); packet != all_packets.end(); ++packet) + { + rng_value ^= packet->crc; + } + } + + // Get a pseudo-random number. + uint32_t rand() + { + // Constants from "Numerical recipes in 'C'" + return rng_value = 1664525 * rng_value + 1013904223; + } + + // configuration + const CONFIGURATION_T configuration; + + // Error logger. + class ERROR_LOGGER_T error_logger; + + // The status from the last call made + VC_CONTAINER_STATUS_T status; + + // Pointer to the file being processed + VC_CONTAINER_T *p_ctx; + + // All the packets in the file + ALL_PACKETS_T all_packets; + + // All the streams + std::set all_streams; + + // All the streams and all the packets in them with a PTS, whether or not they are keyframes. + STREAM_TIMED_PACKETS_T all_pts_packets; + + // Subset of the above that hold key frames. + STREAM_TIMED_PACKETS_T all_key_packets; + + // Places where a seek without VC_CONTAINER_SEEK_FLAG_FORWARD can go to. A subset of all_key_packets. + STREAM_TIMED_PACKETS_T reverse_index_positions; + + // Places where a seek with VC_CONTAINER_SEEK_FLAG_FORWARD can go to. + STREAM_TIMED_PACKETS_T forward_index_positions; + + // The first stream containing video packets + STREAM_T video_stream; + + // The stored value of the RNG + uint32_t rng_value; +}; + +int main(int argc, char** argv) +{ + // Read and parse the configuration information from the command line. + TESTER_T test(argc, argv); + + // Run the tests and return their error code + return test.run(); +} diff --git a/containers/test/check_frame_int.c b/containers/test/check_frame_int.c new file mode 100755 index 0000000..1819c47 --- /dev/null +++ b/containers/test/check_frame_int.c @@ -0,0 +1,348 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_io.h" + +#define BUFFER_SIZE 256*1024 +#define MAX_TRACKS 16 +#define MAX_SEEKS 16 + +static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader); +static int container_test_parse_cmdline(int argc, char **argv); + +static const char *psz_in = 0; +static long packets_num = 0; +static long track_num = -1; +static int fps = 30; +static int margin = 5; // margin for frame interval, percent + +static struct +{ + uint32_t mapping; + uint32_t frames; + uint32_t packets; + uint64_t bytes; + uint32_t frame_size; + int64_t first_dts; + int64_t first_pts; + int64_t last_dts; + int64_t last_pts; +} tracks[MAX_TRACKS]; + +static int32_t verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; + +/*****************************************************************************/ +int main(int argc, char **argv) +{ + int retval = 0; + VC_CONTAINER_T *p_ctx = 0; + VC_CONTAINER_STATUS_T status; + unsigned int i; + uint8_t *buffer = malloc(BUFFER_SIZE); + int32_t interval; + int64_t last_packet_pts = -1; + int32_t max_interval = 0, max_interval_after_first = 0; + int fail = 0; + + if(container_test_parse_cmdline(argc, argv)) + goto error_silent; + + LOG_INFO (0, "Require that no frame interval greater than 1/%d seconds (%d%% margin)", fps, margin); + + /* Set the general verbosity */ + vc_container_log_set_verbosity(0, verbosity); + vc_container_log_set_default_verbosity(verbosity); + + p_ctx = vc_container_open_reader(psz_in, &status, 0, 0); + + if(!p_ctx) + { + LOG_ERROR(0, "error opening file %s (%i)", psz_in, status); + goto error; + } + + if (verbosity & VC_CONTAINER_LOG_DEBUG) + { + container_test_info(p_ctx, true); + } + LOG_INFO (0, "Search for video track only"); + + /* Disabling tracks which are not requested and enable packetisation if requested */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; + track->is_enabled = (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO); + } + + + LOG_DEBUG(0, "TEST start reading"); + for(i = 0; !packets_num || (long)i < packets_num; i++) + { + VC_CONTAINER_PACKET_T packet = {0}; + int32_t frame_num = 0; + + status = vc_container_read(p_ctx, &packet, VC_CONTAINER_READ_FLAG_INFO); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST info status: %i", status); break;} + + if(packet.track < MAX_TRACKS) + { + if((packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)) + { + tracks[packet.track].frames++; + tracks[packet.track].frame_size = 0; + } + frame_num = tracks[packet.track].frames; + } + + tracks[packet.track].frame_size += packet.size; + +// LOG_DEBUG(0, "packet info: track %i, size %i/%i/%i, pts %"PRId64", flags %x%s, num %i", +// packet.track, packet.size, packet.frame_size, tracks[packet.track].frame_size, packet.pts, packet.flags, +// (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "", +// frame_num-1); + + if (last_packet_pts != -1) + interval = packet.pts - last_packet_pts; + else + interval = 0; // packet.pts; + last_packet_pts = packet.pts; + max_interval = MAX (max_interval, interval); + if (i >= 2) + max_interval_after_first = MAX (max_interval_after_first, interval); + + /* Check if interval (in us) exceeds 1/fps, with percentage margin */ + if (interval * fps > 1e4 * (100+margin)) + { + LOG_INFO (0, "Frame %d, interval %.3f FAILED", i, interval / 1000.0f); + fail = 1; + } + + LOG_DEBUG (0, "Frame %d, interval %.3f", i, interval / 1000.0f); + + if(track_num >= 0 && packet.track != (uint32_t)track_num) + { + status = vc_container_read(p_ctx, 0, VC_CONTAINER_READ_FLAG_SKIP); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST skip status: %i", status); break;} + continue; + } + + packet.buffer_size = BUFFER_SIZE; + packet.data = buffer; + packet.size = 0; + status = vc_container_read(p_ctx, &packet, 0); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST read status: %i", status); break;} + + // LOG_DEBUG(0, "packet: track %i, size %i, pts %"PRId64", flags %x", packet.track, packet.size, packet.pts, packet.flags); + + if (tracks[packet.track].packets) + { + tracks[packet.track].last_dts = packet.dts; + tracks[packet.track].last_pts = packet.pts; + } else { + tracks[packet.track].first_dts = packet.dts; + tracks[packet.track].first_pts = packet.pts; + } + + if(packet.track < MAX_TRACKS) + { + tracks[packet.track].packets++; + tracks[packet.track].bytes += packet.size; + } + + } + LOG_DEBUG(0, "TEST stop reading"); + + /* Output stats */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + LOG_INFO(0, "track %u: read %u samples in %u packets for a total of %"PRIu64" bytes", + i, tracks[i].frames, tracks[i].packets, tracks[i].bytes); + LOG_INFO(0, "Starting at %"PRId64"us (decode at %"PRId64"us), ending at %"PRId64"us (decode at %"PRId64"us)", + tracks[i].first_pts, tracks[i].first_dts, tracks[i].last_pts, tracks[i].last_dts); + } + + LOG_INFO (0, "---\nMax interval = %.3f ms; max interval (after first) = %.3f ms\n", (float) max_interval / 1000.0, (float) max_interval_after_first / 1000.0); + + end: + if(p_ctx) vc_container_close(p_ctx); + free(buffer); + +#ifdef _MSC_VER + getchar(); +#endif + + retval = fail; + if (fail) + { + LOG_INFO (0, "TEST FAILED: highest frame interval = %.3f ms", max_interval / 1000.0f); + } + else + LOG_INFO (0, "TEST PASSED"); + return retval; + + error: + LOG_ERROR(0, "TEST FAILED TO RUN"); + error_silent: + retval = -1; + goto end; +} + +static int container_test_parse_cmdline(int argc, char **argv) +{ + int i, j, k; + int32_t *p_verbosity; + + /* Parse the command line arguments */ + for(i = 1; i < argc; i++) + { + if(!argv[i]) continue; + + if(argv[i][0] != '-') + { + /* Not an option argument so will be the input URI */ + psz_in = argv[i]; + continue; + } + + /* We are now dealing with command line options */ + switch(argv[i][1]) + { + case 'v': + j = 2; + p_verbosity = &verbosity; + *p_verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; + for(k = 0; k < 2 && argv[i][j+k] == 'v'; k++) + *p_verbosity = (*p_verbosity << 1) | 1 ; + break; + case 'f': + if(i+1 == argc || !argv[i+1]) goto invalid_option; + fps = strtol(argv[++i], 0, 0); + break; + case 'h': goto usage; + default: goto invalid_option; + } + continue; + } + + /* Sanity check that we have at least an input uri */ + if(!psz_in) + { + LOG_ERROR(0, "missing uri argument"); + goto usage; + } + + return 0; + + invalid_option: + LOG_ERROR(0, "invalid command line option (%s)", argv[i]); + + usage: + psz_in = strrchr(argv[0], '\\'); if(psz_in) psz_in++; + if(!psz_in) {psz_in = strrchr(argv[0], '/'); if(psz_in) psz_in++;} + if(!psz_in) psz_in = argv[0]; + LOG_INFO(0, ""); + LOG_INFO(0, "usage: %s [options] uri", psz_in); + LOG_INFO(0, "options list:"); + LOG_INFO(0, " -vxx : general verbosity level (replace xx with a number of \'v\')"); + LOG_INFO(0, " -f : required frame rate/second (frame interval must not exceed 1/f)"); + LOG_INFO(0, " -h : help"); + return 1; +} + +static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader) +{ + const char *name_type; + unsigned int i; + + LOG_INFO(0, ""); + if(b_reader) LOG_INFO(0, "----Reader Information----"); + else LOG_INFO(0, "----Writer Information----"); + + LOG_INFO(0, "duration: %2.2fs, size: %"PRId64, ctx->duration/1000000.0, ctx->size); + LOG_INFO(0, "capabilities: %x", ctx->capabilities); + LOG_INFO(0, ""); + + for(i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_AUDIO: name_type = "audio"; break; + case VC_CONTAINER_ES_TYPE_VIDEO: name_type = "video"; break; + case VC_CONTAINER_ES_TYPE_SUBPICTURE: name_type = "subpicture"; break; + default: name_type = "unknown"; break; + } + + if (!strcmp (name_type, "video")) + { + LOG_INFO(0, "track: %i, type: %s, fourcc: %4.4s", i, name_type, (char *)&track->format->codec); + LOG_INFO(0, " bitrate: %i, framed: %i, enabled: %i", track->format->bitrate, + !!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED), track->is_enabled); + LOG_INFO(0, " extra data: %i, %p", track->format->extradata_size, track->format->extradata); + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_VIDEO: + LOG_INFO(0, " width: %i, height: %i, (%i,%i,%i,%i)", + track->format->type->video.width, track->format->type->video.height, + track->format->type->video.x_offset, track->format->type->video.y_offset, + track->format->type->video.visible_width, track->format->type->video.visible_height); + LOG_INFO(0, " pixel aspect ratio: %i/%i, frame rate: %i/%i", + track->format->type->video.par_num, track->format->type->video.par_den, + track->format->type->video.frame_rate_num, track->format->type->video.frame_rate_den); + break; + + default: break; + } + } + } + + for (i = 0; i < ctx->meta_num; ++i) + { + const char *name, *value; + if (i == 0) LOG_INFO(0, ""); + name = vc_container_metadata_id_to_string(ctx->meta[i]->key); + value = ctx->meta[i]->value; + if(!name) continue; + LOG_INFO(0, "metadata(%i) : %s : %s", i, name, value); + } + + LOG_INFO(0, "--------------------------"); + LOG_INFO(0, ""); + + return 0; +} + + diff --git a/containers/test/crc_32.c b/containers/test/crc_32.c new file mode 100755 index 0000000..1d2dbaa --- /dev/null +++ b/containers/test/crc_32.c @@ -0,0 +1,227 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include + +#ifndef Boolean_T +typedef unsigned Boolean_T; +#endif +#ifndef Error_ +#define Error_ 1 +#endif + +#ifndef Success_ +#define Success_ 0 +#endif + +#define UPDC32(octet,crc) (crc_32_tab[((crc)\ + ^ ((uint8_t)octet)) & 0xff] ^ ((crc) >> 8)) + +#ifdef __TURBOC__ + #pragma warn -cln +#endif + +/**********************************************************************\ +|* Demonstration program to compute the 32-bit CRC used as the frame *| +|* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| +|* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| +|* protocol). The 32-bit FCS was added via the Federal Register, *| +|* 1 June 1982, p.23798. I presume but don't know for certain that *| +|* this polynomial is or will be included in CCITT V.41, which *| +|* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| +|* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| +|* errors by a factor of 10^-5 over 16-bit FCS. *| +\**********************************************************************/ + +/* Need an unsigned type capable of holding 32 bits; */ + +typedef uint32_t UNS_32_BITS; + +/* Copyright (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction.*/ + +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ + +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ + +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* 1. The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* */ +/* 2. The CRC accumulation logic is the same for all CRC polynomials, */ +/* be they sixteen or thirty-two bits wide. You simply choose the */ +/* appropriate table. Alternatively, because the table can be */ +/* generated at runtime, you can start by generating the table for */ +/* the polynomial in question and use exactly the same "updcrc", */ +/* if your application needn't simultaneously handle two CRC */ +/* polynomials. (Note, however, that XMODEM is strange.) */ +/* */ +/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ +/* of course, 32-bit entries work OK if the high 16 bits are zero. */ +/* */ +/* 4. The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ + +static UNS_32_BITS crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ +0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, +0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, +0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, +0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, +0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, +0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, +0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, +0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, +0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, +0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, +0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, +0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, +0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, +0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, +0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, +0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, +0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, +0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, +0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, +0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, +0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, +0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, +0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, +0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, +0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, +0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, +0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, +0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, +0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, +0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, +0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, +0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, +0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, +0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, +0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, +0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, +0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, +0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, +0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, +0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, +0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, +0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, +0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +uint32_t updateCRC32(unsigned char ch, uint32_t crc) +{ + return UPDC32(ch, crc); +} + +Boolean_T crc32file(char *name, uint32_t *crc, long *charcnt) +{ + register FILE *fin; + register uint32_t oldcrc32; + register int c; + + oldcrc32 = 0xFFFFFFFF; *charcnt = 0; +#ifdef MSDOS + if ((fin=fopen(name, "rb"))==NULL) +#else + if ((fin=fopen(name, "r"))==NULL) +#endif + { + perror(name); + return Error_; + } + while ((c=getc(fin))!=EOF) + { + ++*charcnt; + oldcrc32 = UPDC32(c, oldcrc32); + } + + if (ferror(fin)) + { + perror(name); + *charcnt = -1; + } + fclose(fin); + + *crc = oldcrc32 = ~oldcrc32; + + return Success_; +} + +uint32_t crc32buf(const uint8_t *buf, size_t len) +{ + register uint32_t oldcrc32; + + oldcrc32 = 0xFFFFFFFF; + + for ( ; len; --len, ++buf) + { + oldcrc32 = UPDC32(*buf, oldcrc32); + } + + return ~oldcrc32; +} + +#ifdef TEST + +main(int argc, char *argv[]) +{ + uint32_t crc; + long charcnt; + register errors = 0; + + while(--argc > 0) + { + errors |= crc32file(*++argv, &crc, &charcnt); + printf("%08lX %7ld %s\n", crc, charcnt, *argv); + } + return(errors != 0); +} + +#endif /* TEST */ diff --git a/containers/test/datagram_receiver.c b/containers/test/datagram_receiver.c new file mode 100755 index 0000000..30d661f --- /dev/null +++ b/containers/test/datagram_receiver.c @@ -0,0 +1,80 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/net/net_sockets.h" + +int main(int argc, char **argv) +{ + VC_CONTAINER_NET_T *sock; + vc_container_net_status_t status; + char *buffer; + size_t buffer_size; + size_t received; + + if (argc < 2) + { + printf("Usage:\n%s \n", argv[0]); + return 1; + } + + sock = vc_container_net_open(NULL, argv[1], 0, &status); + if (!sock) + { + printf("vc_container_net_open failed: %d\n", status); + return 2; + } + + buffer_size = vc_container_net_maximum_datagram_size(sock); + buffer = (char *)malloc(buffer_size); + if (!buffer) + { + vc_container_net_close(sock); + printf("Failure allocating buffer\n"); + return 3; + } + + while ((received = vc_container_net_read(sock, buffer, buffer_size)) != 0) + { + char *ptr = buffer; + + while (received--) + putchar(*ptr++); + } + + if (vc_container_net_status(sock) != VC_CONTAINER_NET_SUCCESS) + { + printf("vc_container_net_read failed: %d\n", vc_container_net_status(sock)); + } + + vc_container_net_close(sock); + + return 0; +} diff --git a/containers/test/datagram_sender.c b/containers/test/datagram_sender.c new file mode 100755 index 0000000..6ed7b15 --- /dev/null +++ b/containers/test/datagram_sender.c @@ -0,0 +1,77 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/net/net_sockets.h" + +int main(int argc, char **argv) +{ + VC_CONTAINER_NET_T *sock; + vc_container_net_status_t status; + char *buffer; + size_t buffer_size; + + if (argc < 3) + { + printf("Usage:\n%s
\n", argv[0]); + return 1; + } + + sock = vc_container_net_open(argv[1], argv[2], 0, &status); + if (!sock) + { + printf("vc_container_net_open failed: %d\n", status); + return 2; + } + + buffer_size = vc_container_net_maximum_datagram_size(sock); + buffer = (char *)malloc(buffer_size); + if (!buffer) + { + vc_container_net_close(sock); + printf("Failure allocating buffer\n"); + return 3; + } + + printf("Don't enter more than %d characters in one line!\n", (int)buffer_size); + + while (fgets(buffer, buffer_size, stdin)) + { + if (!vc_container_net_write(sock, buffer, strlen(buffer))) + { + printf("vc_container_net_write failed: %d\n", vc_container_net_status(sock)); + break; + } + } + + vc_container_net_close(sock); + + return 0; +} diff --git a/containers/test/dump_pktfile.c b/containers/test/dump_pktfile.c new file mode 100755 index 0000000..eddf197 --- /dev/null +++ b/containers/test/dump_pktfile.c @@ -0,0 +1,147 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#define PACKET_BUFFER_SIZE 32768 + +enum { + SUCCESS = 0, + SHOWED_USAGE, + FAILED_TO_OPEN_PKTFILE, + FAILED_TO_OPEN_OUTPUT_FILE, + BYTE_ORDER_MARK_MISSING, + INVALID_BYTE_ORDER_MARK, + MEMORY_ALLOCATION_FAILURE, + PACKET_TOO_BIG, +}; + +/** Native byte order word */ +#define NATIVE_BYTE_ORDER 0x50415753U +/** Reverse of native byte order - need to swap bytes around */ +#define SWAP_BYTE_ORDER 0x53574150U + +static unsigned long reverse_byte_order( unsigned long value ) +{ + /* Reverse the order of the bytes in the word */ + return ((value << 24) | ((value & 0xFF00) << 8) | ((value >> 8) & 0xFF00) | (value >> 24)); +} + +int main(int argc, char **argv) +{ + int status = SUCCESS; + FILE *pktfile = NULL, *output_file = NULL; + uint32_t byte_order, packet_size, packet_read; + char *buffer = NULL; + + if (argc < 2) + { + printf("\ +Usage:\n\ + %s []\n\ + is the file to read.\n\ +, if given, will receive the unpacketized data.\n", argv[0]); + status = SHOWED_USAGE; + goto end_program; + } + + pktfile = fopen(argv[1], "rb"); + if (!pktfile) + { + printf("Failed to open pkt file <%s> for reading.\n", argv[1]); + status = FAILED_TO_OPEN_PKTFILE; + goto end_program; + } + + if (fread(&byte_order, 1, sizeof(byte_order), pktfile) != sizeof(byte_order)) + { + printf("Failed to read byte order header from pkt file.\n"); + status = BYTE_ORDER_MARK_MISSING; + goto end_program; + } + + if (byte_order != NATIVE_BYTE_ORDER && byte_order != SWAP_BYTE_ORDER) + { + printf("Invalid byte order header: 0x%08x (%u)\n", byte_order, byte_order); + status = INVALID_BYTE_ORDER_MARK; + goto end_program; + } + + buffer = (char *)malloc(PACKET_BUFFER_SIZE); + if (!buffer) + { + printf("Memory allocation failed\n"); + status = MEMORY_ALLOCATION_FAILURE; + goto end_program; + } + + if (argc > 2) + { + output_file = fopen(argv[2], "wb"); + if (!output_file) + { + printf("Failed to open <%s> for output.\n", argv[2]); + status = FAILED_TO_OPEN_OUTPUT_FILE; + goto end_program; + } + } + + while (fread(&packet_size, 1, sizeof(packet_size), pktfile) == sizeof(packet_size)) + { + if (byte_order == SWAP_BYTE_ORDER) + packet_size = reverse_byte_order(packet_size); + + if (packet_size >= PACKET_BUFFER_SIZE) + { + printf("*** Packet size is bigger than buffer (%u > %u)\n", packet_size, PACKET_BUFFER_SIZE - 1); + status = PACKET_TOO_BIG; + goto end_program; + } + + packet_read = fread(buffer, 1, packet_size, pktfile); + + if (output_file) + { + fwrite(buffer, 1, packet_size, output_file); + } else { + buffer[packet_size] = '\0'; + printf("%s", buffer); + getchar(); + } + + if (packet_read != packet_size) + printf("*** Invalid packet size (expected %u, got %u)\n", packet_size, packet_read); + } + +end_program: + if (pktfile) fclose(pktfile); + if (output_file) fclose(output_file); + if (buffer) free(buffer); + return -status; +} diff --git a/containers/test/nb_io.h b/containers/test/nb_io.h new file mode 100755 index 0000000..2058fe6 --- /dev/null +++ b/containers/test/nb_io.h @@ -0,0 +1,47 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _NB_IO_H_ +#define _NB_IO_H_ + +/** Set whether console input is non-blocking or not. + * + * \param enable Pass true to set input as non-blocking, false to set it as blocking again.*/ +void nb_set_nonblocking_input(int enable); + +/** \return Non-zero when there is a character available to read using nb_get_char(). */ +int nb_char_available(void); + +/** \return The next character available from the console, or zero. */ +char nb_get_char(void); + +/** Put the character out to the console. + * + * \param ch The character to be output. */ +void nb_put_char(char ch); + +#endif /* _NB_IO_H_ */ diff --git a/containers/test/nb_io_unix.c b/containers/test/nb_io_unix.c new file mode 100755 index 0000000..63e1a1f --- /dev/null +++ b/containers/test/nb_io_unix.c @@ -0,0 +1,80 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "nb_io.h" + +void nb_set_nonblocking_input(int enable) +{ + struct termios ttystate; + + //get the terminal state + tcgetattr(STDIN_FILENO, &ttystate); + + if (enable) + { + //turn off canonical mode + ttystate.c_lflag &= ~ICANON; + //minimum of number input read. + ttystate.c_cc[VMIN] = 1; + } + else + { + //turn on canonical mode + ttystate.c_lflag |= ICANON; + } + + //set the terminal attributes. + tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); +} + +int nb_char_available(void) +{ + struct timeval tv; + fd_set fds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); + return (FD_ISSET(0, &fds)); +} + +char nb_get_char(void) +{ + return getchar(); +} + +void nb_put_char(char ch) +{ + putchar(ch); +} diff --git a/containers/test/nb_io_win32.c b/containers/test/nb_io_win32.c new file mode 100755 index 0000000..a402bcb --- /dev/null +++ b/containers/test/nb_io_win32.c @@ -0,0 +1,53 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include /* For _kbhit() */ + +#include "nb_io.h" + +void nb_set_nonblocking_input(int enable) +{ + /* No need to do anything in Win32 */ + (void)enable; +} + +int nb_char_available() +{ + return _kbhit(); +} + +char nb_get_char() +{ + char c = _getch(); + _putch(c); /* Echo back */ + return c; +} + +void nb_put_char(char ch) +{ + _putch(ch); +} diff --git a/containers/test/rtp_decoder.c b/containers/test/rtp_decoder.c new file mode 100755 index 0000000..c089576 --- /dev/null +++ b/containers/test/rtp_decoder.c @@ -0,0 +1,449 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "containers/containers.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_io.h" + +#include "nb_io.h" + +#define MAXIMUM_BUFFER_SIZE 65000 +#define MINIMUM_BUFFER_SPACE 1500 + +#define INITIAL_READ_BUFFER_SIZE 8000 +#define MAXIMUM_READ_BUFFER_SIZE 64000 + +#define BYTES_PER_ROW 32 + +#define HAS_PADDING 0x20 +#define HAS_EXTENSION 0x10 +#define CSRC_COUNT_MASK 0x0F + +#define HAS_MARKER 0x80 +#define PAYLOAD_TYPE_MASK 0x7F + +#define EXTENSION_LENGTH_MASK 0x0000FFFF +#define EXTENSION_ID_SHIFT 16 + +#define LOWEST_VERBOSITY 1 +#define BASIC_HEADER_VERBOSITY 2 +#define FULL_HEADER_VERBOSITY 3 +#define FULL_PACKET_VERBOSITY 4 + +#define ESCAPE_CHARACTER 0x1B + +static bool seen_first_packet; +static uint16_t expected_next_seq_num; + +static bool do_print_usage; +static uint32_t verbosity; +static const char *read_uri; +static const char *packet_save_file; +static bool packet_save_is_pktfile; + +static uint16_t network_to_host_16(const uint8_t *buffer) +{ + return (buffer[0] << 8) | buffer[1]; +} + +static uint32_t network_to_host_32(const uint8_t *buffer) +{ + return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; +} + +/** Avoid alignment problems when writing a word value to the buffer */ +static void store_u32(uint8_t *buffer, uint32_t value) +{ + buffer[0] = (uint8_t)value; + buffer[1] = (uint8_t)(value >> 8); + buffer[2] = (uint8_t)(value >> 16); + buffer[3] = (uint8_t)(value >> 24); +} + +/** Avoid alignment problems when reading a word value from the buffer */ +static uint32_t fetch_u32(uint8_t *buffer) +{ + return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]; +} + +static bool marker_bit_set(const uint8_t *buffer, size_t buffer_len) +{ + if (buffer_len < 2) + return false; + + return (buffer[1] & HAS_MARKER); +} + +static void dump_bytes(const uint8_t *buffer, size_t buffer_len) +{ + char dump_str[3 * BYTES_PER_ROW + 1]; + int in_row = 0; + + while (buffer_len--) + { + sprintf(dump_str + 3 * in_row, "%2.2X ", *buffer++); + if (++in_row == BYTES_PER_ROW) + { + LOG_INFO(NULL, dump_str); + in_row = 0; + } + } + + if (in_row) + { + LOG_INFO(NULL, dump_str); + } +} + +static bool decode_packet(const uint8_t *buffer, size_t buffer_len) +{ + uint8_t flags; + uint8_t payload_type; + uint16_t seq_num; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc_count; + + if (buffer_len < 12) + { + LOG_ERROR(NULL, "Packet too small: basic header missing"); + return false; + } + + flags = buffer[0]; + payload_type = buffer[1]; + seq_num = network_to_host_16(buffer + 2); + timestamp = network_to_host_32(buffer + 4); + ssrc = network_to_host_32(buffer + 8); + + if (seen_first_packet && seq_num != expected_next_seq_num) + { + int16_t missing_packets = seq_num - expected_next_seq_num; + + LOG_INFO(NULL, "*** Sequence break, expected %hu, got %hu ***", expected_next_seq_num, seq_num); + if (missing_packets > 0) + LOG_INFO(NULL, "*** Jumped forward %hd packets ***", missing_packets); + else + LOG_INFO(NULL, "*** Jumped backward %hd packets ***", -missing_packets); + } + seen_first_packet = true; + expected_next_seq_num = seq_num + 1; + + /* Dump the basic header information */ + if (verbosity >= BASIC_HEADER_VERBOSITY) + { + LOG_INFO(NULL, "Version: %d\nPayload type: %d%s\nSequence: %d\nTimestamp: %u\nSSRC: 0x%8.8X", + flags >> 6, payload_type & PAYLOAD_TYPE_MASK, + (const char *)((payload_type & HAS_MARKER) ? " (M)" : ""), + seq_num, timestamp, ssrc); + } + + buffer += 12; + buffer_len -= 12; + + if (verbosity >= FULL_HEADER_VERBOSITY) + { + /* Dump the CSRCs, if any */ + csrc_count = flags & CSRC_COUNT_MASK; + if (csrc_count) + { + uint32_t ii; + + if (buffer_len < (csrc_count * 4)) + { + LOG_ERROR(NULL, "Packet too small: CSRCs missing"); + return false; + } + + LOG_INFO(NULL, "CSRCs:"); + for (ii = 0; ii < csrc_count; ii++) + { + LOG_INFO(NULL, " 0x%8.8X", network_to_host_32(buffer)); + buffer += 4; + buffer_len -= 4; + } + } + + /* Dump any extension, if present */ + if (flags & HAS_EXTENSION) + { + uint32_t extension_hdr; + uint32_t extension_id; + size_t extension_len; + + if (buffer_len < 4) + { + LOG_ERROR(NULL, "Packet too small: extension header missing"); + return false; + } + + extension_hdr = network_to_host_32(buffer); + buffer += 4; + buffer_len -= 4; + + extension_len = (size_t)(extension_hdr & EXTENSION_LENGTH_MASK); + extension_id = extension_hdr >> EXTENSION_ID_SHIFT; + + if (buffer_len < extension_len) + { + LOG_ERROR(NULL, "Packet too small: extension content missing"); + return false; + } + + LOG_INFO(NULL, "Extension: 0x%4.4X (%u bytes)", extension_id, (unsigned)extension_len); + dump_bytes(buffer, extension_len); + buffer += extension_len; + buffer_len -= extension_len; + } + } + + /* And finally the payload data */ + if (verbosity >= FULL_PACKET_VERBOSITY) + { + LOG_INFO(NULL, "Data: (%u bytes)", (unsigned)buffer_len); + dump_bytes(buffer, buffer_len); + } + + return true; +} + +static void increase_read_buffer_size(VC_CONTAINER_IO_T *p_ctx) +{ + uint32_t buffer_size = INITIAL_READ_BUFFER_SIZE; + + /* Iteratively enlarge read buffer until either operation fails or maximum is reached. */ + while (vc_container_io_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE, buffer_size) == VC_CONTAINER_SUCCESS) + { + buffer_size <<= 1; /* Double and try again */ + if (buffer_size > MAXIMUM_READ_BUFFER_SIZE) + break; + } +} + +static void parse_command_line(int argc, char **argv) +{ + int arg = 1; + + while (arg < argc) + { + if (*argv[arg] != '-') /* End of options, next should be URI */ + break; + + switch (argv[arg][1]) + { + case 'h': + do_print_usage = true; + break; + case 's': + arg++; + if (arg >= argc) + break; + packet_save_file = argv[arg]; + packet_save_is_pktfile = (strncmp(packet_save_file, "pktfile:", 8) == 0); + break; + case 'v': + { + const char *ptr = &argv[arg][2]; + + verbosity = 1; + while (*ptr++ == 'v') + verbosity++; + } + break; + default: LOG_ERROR(NULL, "Unrecognised option: %s", argv[arg]); return; + } + + arg++; + } + + if (arg < argc) + read_uri = argv[arg]; +} + +static void print_usage(char *program_name) +{ + LOG_INFO(NULL, "Usage:"); + LOG_INFO(NULL, " %s [opts] ", program_name); + LOG_INFO(NULL, "Reads RTP packets from , decodes to standard output."); + LOG_INFO(NULL, "Press the escape key to terminate the program."); + LOG_INFO(NULL, "Options:"); + LOG_INFO(NULL, " -h Print this information"); + LOG_INFO(NULL, " -s x Save packets to URI x"); + LOG_INFO(NULL, " -v Dump standard packet header"); + LOG_INFO(NULL, " -vv Dump entire header"); + LOG_INFO(NULL, " -vvv Dump entire header and data"); +} + +int main(int argc, char **argv) +{ + int result = 0; + uint8_t *buffer = NULL; + VC_CONTAINER_IO_T *read_io = NULL; + VC_CONTAINER_IO_T *write_io = NULL; + VC_CONTAINER_STATUS_T status; + size_t received_bytes; + bool ready = true; + uint32_t available_bytes; + uint8_t *packet_ptr; + + parse_command_line(argc, argv); + + if (do_print_usage || !read_uri) + { + print_usage(argv[0]); + result = 1; goto tidyup; + } + + buffer = (uint8_t *)malloc(MAXIMUM_BUFFER_SIZE); + if (!buffer) + { + LOG_ERROR(NULL, "Allocating %d bytes for the buffer failed", MAXIMUM_BUFFER_SIZE); + result = 2; goto tidyup; + } + + read_io = vc_container_io_open(read_uri, VC_CONTAINER_IO_MODE_READ, &status); + if (!read_io) + { + LOG_ERROR(NULL, "Opening <%s> for read failed: %d", read_uri, status); + result = 3; goto tidyup; + } + + increase_read_buffer_size(read_io); + + if (packet_save_file) + { + write_io = vc_container_io_open(packet_save_file, VC_CONTAINER_IO_MODE_WRITE, &status); + if (!write_io) + { + LOG_ERROR(NULL, "Opening <%s> for write failed: %d", packet_save_file, status); + result = 4; goto tidyup; + } + if (!packet_save_is_pktfile) + { + store_u32(buffer, 0x50415753); + vc_container_io_write(write_io, buffer, sizeof(uint32_t)); + } + } + + /* Use non-blocking I/O for both network and console */ + vc_container_io_control(read_io, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, 20); + nb_set_nonblocking_input(1); + + packet_ptr = buffer; + available_bytes = MAXIMUM_BUFFER_SIZE - sizeof(uint32_t); + while (ready) + { + /* Read a packet and store its length in the word before it */ + received_bytes = vc_container_io_read(read_io, packet_ptr + sizeof(uint32_t), available_bytes); + if (received_bytes) + { + bool packet_has_marker; + + store_u32(packet_ptr, received_bytes); + packet_ptr += sizeof(uint32_t); + packet_has_marker = marker_bit_set(packet_ptr, received_bytes); + packet_ptr += received_bytes; + available_bytes -= received_bytes + sizeof(uint32_t); + + if (packet_has_marker || (available_bytes < MINIMUM_BUFFER_SPACE)) + { + uint8_t *decode_ptr; + + if (write_io && !packet_save_is_pktfile) + { + uint32_t total_bytes = packet_ptr - buffer; + if (vc_container_io_write(write_io, buffer, total_bytes) != total_bytes) + { + LOG_ERROR(NULL, "Error saving packets to file"); + break; + } + if (verbosity >= LOWEST_VERBOSITY) + LOG_INFO(NULL, "Written %u bytes to file", total_bytes); + } + + for (decode_ptr = buffer; decode_ptr < packet_ptr;) + { + received_bytes = fetch_u32(decode_ptr); + decode_ptr += sizeof(uint32_t); + + if (write_io && packet_save_is_pktfile) + { + if (vc_container_io_write(write_io, buffer, received_bytes) != received_bytes) + { + LOG_ERROR(NULL, "Error saving packets to file"); + break; + } + if (verbosity >= LOWEST_VERBOSITY) + LOG_INFO(NULL, "Written %u bytes to file", received_bytes); + } + + if (!decode_packet(decode_ptr, received_bytes)) + { + LOG_ERROR(NULL, "Failed to decode packet"); + break; + } + decode_ptr += received_bytes; + } + + /* Reset to start of buffer */ + packet_ptr = buffer; + available_bytes = MAXIMUM_BUFFER_SIZE - sizeof(uint32_t); + } + } + + if (nb_char_available()) + { + if (nb_get_char() == ESCAPE_CHARACTER) + ready = false; + } + + switch (read_io->status) + { + case VC_CONTAINER_SUCCESS: + case VC_CONTAINER_ERROR_CONTINUE: + break; + default: + ready = false; + } + } + + nb_set_nonblocking_input(0); + +tidyup: + if (write_io) + vc_container_io_close(write_io); + if (read_io) + vc_container_io_close(read_io); + if (buffer) + free(buffer); + + return result; +} diff --git a/containers/test/stream_client.c b/containers/test/stream_client.c new file mode 100755 index 0000000..b1c015e --- /dev/null +++ b/containers/test/stream_client.c @@ -0,0 +1,145 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "containers/net/net_sockets.h" + +#include "nb_io.h" + +#define MAX_BUFFER_LEN 1024 +/* Time in milliseconds to yield when nothing is happening */ +#define YIELD_PERIOD_MS 33 + + +static vc_container_net_status_t local_net_control(VC_CONTAINER_NET_T *sock, + vc_container_net_control_t operation, ...) +{ + vc_container_net_status_t result; + va_list args; + + va_start(args, operation); + result = vc_container_net_control(sock, operation, args); + va_end(args); + + return result; +} + +int main(int argc, char **argv) +{ + VC_CONTAINER_NET_T *sock; + vc_container_net_status_t status; + char send_buffer[MAX_BUFFER_LEN]; + char recv_buffer[MAX_BUFFER_LEN]; + int ready = 1; + int to_send = 0; + size_t received; + + if (argc < 3) + { + printf("Usage:\n%s
\n", argv[0]); + return 1; + } + + sock = vc_container_net_open(argv[1], argv[2], VC_CONTAINER_NET_OPEN_FLAG_STREAM, &status); + if (!sock) + { + printf("vc_container_net_open failed: %d\n", status); + return 2; + } + + /* Use non-blocking I/O for both network and console */ + local_net_control(sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, YIELD_PERIOD_MS); + nb_set_nonblocking_input(1); + + while (ready) + { + if (nb_char_available()) + { + char c = nb_get_char(); + + if (c == 26) /* CTRL+Z */ + break; + + send_buffer[to_send++] = c; + + if (c == '\r') /* Translate CR into CRLF */ + { + c = '\n'; + nb_put_char(c); + send_buffer[to_send++] = c; + } + + if (c == '\n' || to_send == sizeof(send_buffer) - 1) /* Allow for next character needing translation */ + { + int already_sent = 0, sent; + + /* Send a line at a time */ + while (to_send) + { + sent = vc_container_net_write(sock, send_buffer + already_sent, to_send); + if (!sent) + { + printf("vc_container_net_write failed: %d\n", vc_container_net_status(sock)); + to_send = 0; + ready = 0; + break; + } + to_send -= sent; + already_sent += sent; + } + } + } + + /* Read back and print any data from the server */ + received = vc_container_net_read(sock, recv_buffer, sizeof(recv_buffer) - 1); + if (received) + { + char *ptr = recv_buffer; + + while (received--) + nb_put_char(*ptr++); + } else { + vc_container_net_status_t net_status = vc_container_net_status(sock); + + if (net_status != VC_CONTAINER_NET_ERROR_TIMED_OUT && net_status != VC_CONTAINER_NET_SUCCESS) + { + printf("vc_container_net_read failed: %d\n", net_status); + ready = 0; + } + } + } + + nb_set_nonblocking_input(0); + + vc_container_net_close(sock); + + return 0; +} diff --git a/containers/test/stream_server.c b/containers/test/stream_server.c new file mode 100755 index 0000000..8dead8f --- /dev/null +++ b/containers/test/stream_server.c @@ -0,0 +1,148 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "containers/net/net_sockets.h" + +#define MAX_BUFFER_LEN 1024 +#define MAX_NAME_LEN 256 +#define MAX_PORT_LEN 32 + +int main(int argc, char **argv) +{ + VC_CONTAINER_NET_T *server_sock, *sock; + vc_container_net_status_t status; + char buffer[MAX_BUFFER_LEN]; + char name[MAX_NAME_LEN]; + unsigned short port; + int ii, connections = 1; + size_t received; + + if (argc < 2) + { + printf("Usage:\n%s []\n", argv[0]); + return 1; + } + + server_sock = vc_container_net_open(NULL, argv[1], VC_CONTAINER_NET_OPEN_FLAG_STREAM, &status); + if (!server_sock) + { + printf("vc_container_net_open failed: %d\n", status); + return 2; + } + + if (argc > 2) + { + sscanf(argv[2], "%d", &connections); + } + + status = vc_container_net_listen(server_sock, connections); + if (status != VC_CONTAINER_NET_SUCCESS) + { + printf("vc_container_net_listen failed: %d\n", status); + vc_container_net_close(server_sock); + return 3; + } + + for (ii = 0; ii < connections; ii++) + { + status = vc_container_net_accept(server_sock, &sock); + if (status != VC_CONTAINER_NET_SUCCESS) + { + printf("vc_container_net_accept failed: %d\n", status); + vc_container_net_close(server_sock); + return 3; + } + + strcpy(name, ""); + vc_container_net_get_client_name(sock, name, sizeof(name)); + vc_container_net_get_client_port(sock, &port); + printf("Connection from %s:%hu\n", name, port); + + while ((received = vc_container_net_read(sock, buffer, sizeof(buffer) - 1)) != 0) + { + char *ptr = buffer; + size_t jj; + + printf("Rx:"); + + /* Flip case and echo data back to client */ + for (jj = 0; jj < received; jj++, ptr++) + { + char c = *ptr; + + putchar(c); + if (isalpha((unsigned char)c)) + *ptr ^= 0x20; /* Swaps case of ASCII alphabetic characters */ + } + + ptr = buffer; + + printf("Tx:"); + while (received) + { + size_t sent; + + sent = vc_container_net_write(sock, ptr, received); + if (!sent) + { + status = vc_container_net_status(sock); + printf("vc_container_net_write failed: %d\n", status); + break; + } + + /* Print out bytes actually sent */ + while (sent--) + { + received--; + putchar(*ptr++); + } + } + } + + status = vc_container_net_status(sock); + + if (status != VC_CONTAINER_NET_SUCCESS && status != VC_CONTAINER_NET_ERROR_CONNECTION_LOST) + break; + + vc_container_net_close(sock); + } + + if (status != VC_CONTAINER_NET_SUCCESS) + { + printf("vc_container_net_read failed: %d\n", status); + } + + vc_container_net_close(server_sock); + + return 0; +} diff --git a/containers/test/test.c b/containers/test/test.c new file mode 100755 index 0000000..f6a5b08 --- /dev/null +++ b/containers/test/test.c @@ -0,0 +1,623 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_io.h" + +#define BUFFER_SIZE 256*1024 +#define MAX_TRACKS 16 +#define MAX_SEEKS 16 + +static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader); +static int container_test_parse_cmdline(int argc, char **argv); + +/* Client i/o wrapper */ +static VC_CONTAINER_IO_T *client_io_open(const char *, VC_CONTAINER_STATUS_T *); + +static bool b_info = 0, b_seek = 0, b_dump = 0; +static bool b_audio = 1, b_video = 1, b_subs = 1, b_errorcode = 1; +static const char *psz_in = 0, *psz_out = 0; +static long packets_num = 0; +static long track_num = -1; +static FILE *dump_file = 0; +static bool b_client_io = 0; +static bool b_packetize = 0; + +static struct +{ + uint32_t mapping; + uint32_t frames; + uint32_t packets; + uint64_t bytes; + uint32_t frame_size; + int64_t first_dts; + int64_t first_pts; + int64_t last_dts; + int64_t last_pts; +} tracks[MAX_TRACKS]; + +static unsigned int seeks = 0; +static long seek_offsets[MAX_SEEKS]; +static long seek_flags[MAX_SEEKS]; +static int32_t verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; +static int32_t verbosity_input = -1, verbosity_output = -1; + +/*****************************************************************************/ +int main(int argc, char **argv) +{ + int retval = 0; + VC_CONTAINER_T *p_ctx = 0, *p_writer_ctx = 0; + VC_CONTAINER_STATUS_T status; + unsigned int i, j; + uint8_t *buffer = malloc(BUFFER_SIZE); + int64_t seek_time; + + if(container_test_parse_cmdline(argc, argv)) + goto error_silent; + + /* Set the general verbosity */ + vc_container_log_set_verbosity(0, verbosity); + + if(verbosity_input < 0) verbosity_input = verbosity; + if(verbosity_output < 0) verbosity_output = verbosity; + + /* Open a dump file if it was requested */ + if(b_dump) + { + char *psz_dump; + + if (psz_out) + { + psz_dump = vcos_strdup(psz_out); + } else { + psz_dump = strrchr(psz_in, '\\'); if(psz_dump) psz_dump++; + if(!psz_dump) {psz_dump = strrchr(psz_in, '/'); if(psz_dump) psz_dump++;} + if(!psz_dump) psz_dump = vcos_strdup(psz_in); + else psz_dump = vcos_strdup(psz_dump); + psz_dump[strlen(psz_dump)-1] = '1'; + } + dump_file = fopen(psz_dump, "wb"); + if(!dump_file) LOG_ERROR(0, "error opening dump file %s", psz_dump); + else LOG_INFO(0, "data packets will dumped to %s", psz_dump); + free(psz_dump); + if(!dump_file) goto error; + } + + /* Open a writer if an output was requested */ + if(psz_out && !b_dump) + { + vc_container_log_set_default_verbosity(verbosity_output); + p_writer_ctx = vc_container_open_writer(psz_out, &status, 0, 0); + if(!p_writer_ctx) + { + LOG_ERROR(0, "error opening file %s (%i)", psz_out, status); + goto error; + } + } + + vc_container_log_set_default_verbosity(verbosity_input); + + /* Open the container */ + if(b_client_io) + { + VC_CONTAINER_IO_T *p_io; + + LOG_INFO(0, "Using client I/O for %s", psz_in); + p_io = client_io_open(psz_in, &status); + if(!p_io) + { + LOG_ERROR(0, "error creating io for %s (%i)", psz_in, status); + goto error; + } + + p_ctx = vc_container_open_reader_with_io(p_io, psz_in, &status, 0, 0); + if(!p_ctx) + vc_container_io_close(p_io); + } + else + { + p_ctx = vc_container_open_reader(psz_in, &status, 0, 0); + } + + if(!p_ctx) + { + LOG_ERROR(0, "error opening file %s (%i)", psz_in, status); + goto error; + } + + /* Disabling tracks which are not requested and enable packetisation if requested */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; + unsigned int disable = 0; + + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_VIDEO: if(!b_video) disable = 1; break; + case VC_CONTAINER_ES_TYPE_AUDIO: if(!b_audio) disable = 1; break; + case VC_CONTAINER_ES_TYPE_SUBPICTURE: if(!b_subs) disable = 1; break; + default: break; + } + if(disable) + { + track->is_enabled = 0; + LOG_INFO(0, "disabling track: %i, fourcc: %4.4s", i, (char *)&track->format->codec); + } + + if(track->is_enabled && b_packetize && !(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED)) + { + status = vc_container_control(p_ctx, VC_CONTAINER_CONTROL_TRACK_PACKETIZE, i, track->format->codec_variant); + if(status != VC_CONTAINER_SUCCESS) + { + LOG_ERROR(0, "packetization not supported on track: %i, fourcc: %4.4s", i, (char *)&track->format->codec); + track->is_enabled = 0; + } + } + } + + container_test_info(p_ctx, true); + if(b_info) goto end; + + if(p_writer_ctx) + { + LOG_INFO(0, "----Writer Information----"); + for(i = 0; i < p_ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i]; + if(!track->is_enabled) continue; + tracks[p_writer_ctx->tracks_num].mapping = i; + LOG_INFO(0, "adding track: %i, fourcc: %4.4s", i, (char *)&track->format->codec); + status = vc_container_control(p_writer_ctx, VC_CONTAINER_CONTROL_TRACK_ADD, track->format); + if(status) + { + LOG_INFO(0, "unsupported track type (%i, %i)", status, i); + track->is_enabled = 0; /* disable track */ + } + } + if(p_writer_ctx->tracks_num) + { + status = vc_container_control(p_writer_ctx, VC_CONTAINER_CONTROL_TRACK_ADD_DONE); + if(status) LOG_INFO(0, "could not add tracks (%i)", status); + } + LOG_INFO(0, "--------------------------"); + LOG_INFO(0, ""); + } + + for(i = 0; i < seeks; i++) + { + LOG_DEBUG(0, "TEST seek to %ims", seek_offsets[i]); + seek_time = ((int64_t)(seek_offsets[i])) * 1000; + status = vc_container_seek(p_ctx, &seek_time, VC_CONTAINER_SEEK_MODE_TIME, seek_flags[i]); + LOG_DEBUG(0, "TEST seek done (%i) to %ims", status, (int)(seek_time/1000)); + } + + LOG_DEBUG(0, "TEST start reading"); + for(i = 0; !packets_num || (long)i < packets_num; i++) + { + VC_CONTAINER_PACKET_T packet = {0}; + int32_t frame_num = 0; + + status = vc_container_read(p_ctx, &packet, VC_CONTAINER_READ_FLAG_INFO); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST info status: %i", status); break;} + + if(packet.track < MAX_TRACKS) + { + if((packet.flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)) + { + tracks[packet.track].frames++; + tracks[packet.track].frame_size = 0; + } + frame_num = tracks[packet.track].frames; + } + + tracks[packet.track].frame_size += packet.size; + + LOG_DEBUG(0, "packet info: track %i, size %i/%i/%i, pts %"PRId64", flags %x%s, num %i", + packet.track, packet.size, packet.frame_size, tracks[packet.track].frame_size, packet.pts, packet.flags, + (packet.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? " (keyframe)" : "", + frame_num-1); + + if(track_num >= 0 && packet.track != (uint32_t)track_num) + { + status = vc_container_read(p_ctx, 0, VC_CONTAINER_READ_FLAG_SKIP); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST skip status: %i", status); break;} + continue; + } + + packet.buffer_size = BUFFER_SIZE; + packet.data = buffer; + packet.size = 0; + status = vc_container_read(p_ctx, &packet, 0); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST read status: %i", status); break;} + + LOG_DEBUG(0, "packet: track %i, size %i, pts %"PRId64", flags %x", packet.track, packet.size, packet.pts, packet.flags); + + if (tracks[packet.track].packets) + { + tracks[packet.track].last_dts = packet.dts; + tracks[packet.track].last_pts = packet.pts; + } else { + tracks[packet.track].first_dts = packet.dts; + tracks[packet.track].first_pts = packet.pts; + } + + if(packet.track < MAX_TRACKS) + { + tracks[packet.track].packets++; + tracks[packet.track].bytes += packet.size; + } + + if(dump_file) fwrite(packet.data, packet.size, 1, dump_file); + + if(p_writer_ctx) + { + packet.track = tracks[packet.track].mapping; + status = vc_container_write(p_writer_ctx, &packet); + if(status != VC_CONTAINER_SUCCESS) {LOG_DEBUG(0, "TEST write status: %i", status); break;} + } + } + LOG_DEBUG(0, "TEST stop reading"); + + if(b_seek) + { + LOG_DEBUG(0, "TEST start seeking"); + for(j = 0, seek_time = 100; j < 20; j++) + { + LOG_DEBUG(0, "seeking to %ims", (int)(seek_time/1000)); + status = vc_container_seek(p_ctx, &seek_time, VC_CONTAINER_SEEK_MODE_TIME, VC_CONTAINER_SEEK_FLAG_FORWARD); + LOG_DEBUG(0, "seek done (%i) to %ims", status, (int)(seek_time/1000)); + + for(i = 0; i < 1; i++) + { + VC_CONTAINER_PACKET_T packet = {0}; + packet.buffer_size = BUFFER_SIZE; + packet.data = buffer; + + status = vc_container_read(p_ctx, &packet, 0); + if(status) LOG_DEBUG(0, "TEST read status: %i", status); + if(status == VC_CONTAINER_ERROR_EOS) break; + if(status == VC_CONTAINER_ERROR_CORRUPTED) break; + if(status == VC_CONTAINER_ERROR_FORMAT_INVALID) break; + seek_time = packet.pts + 800000; + } + } + LOG_DEBUG(0, "TEST stop seeking"); + } + + /* Output stats */ + for(i = 0; i < p_ctx->tracks_num; i++) + { + LOG_INFO(0, "track %u: read %u samples in %u packets for a total of %"PRIu64" bytes", + i, tracks[i].frames, tracks[i].packets, tracks[i].bytes); + LOG_INFO(0, "Starting at %"PRId64"us (decode at %"PRId64"us), ending at %"PRId64"us (decode at %"PRId64"us)", + tracks[i].first_pts, tracks[i].first_dts, tracks[i].last_pts, tracks[i].last_dts); + } + + end: + if(p_ctx) vc_container_close(p_ctx); + if(p_writer_ctx) + { + container_test_info(p_writer_ctx, false); + vc_container_close(p_writer_ctx); + } + if(dump_file) fclose(dump_file); + free(buffer); + +#ifdef _MSC_VER + getchar(); +#endif + + LOG_ERROR(0, "TEST ENDED (%i)", retval); + return b_errorcode ? retval : 0; + + error: + LOG_ERROR(0, "TEST FAILED"); + error_silent: + retval = -1; + goto end; +} + +static int container_test_parse_cmdline(int argc, char **argv) +{ + int i, j, k; + int32_t *p_verbosity; + + /* Parse the command line arguments */ + for(i = 1; i < argc; i++) + { + if(!argv[i]) continue; + + if(argv[i][0] != '-') + { + /* Not an option argument so will be the input URI */ + psz_in = argv[i]; + continue; + } + + /* We are now dealing with command line options */ + switch(argv[i][1]) + { + case 'i': b_info = 1; break; + case 'S': b_seek = 1; break; + case 'd': b_dump = 1; break; + case 'c': b_client_io = 1; break; + case 'v': + if(argv[i][2] == 'i') {j = 3; p_verbosity = &verbosity_input;} + else if(argv[i][2] == 'o') {j = 3; p_verbosity = &verbosity_output;} + else {j = 2; p_verbosity = &verbosity;} + *p_verbosity = VC_CONTAINER_LOG_ERROR|VC_CONTAINER_LOG_INFO; + for(k = 0; k < 2 && argv[i][j+k] == 'v'; k++) + *p_verbosity = (*p_verbosity << 1) | 1 ; + break; + case 's': + if(i+1 == argc || !argv[i+1]) goto invalid_option; + if(seeks >= MAX_SEEKS) goto invalid_option; + seek_flags[seeks] = argv[i][2] == 'f' ? VC_CONTAINER_SEEK_FLAG_FORWARD : 0; + seek_offsets[seeks] = strtol(argv[++i], 0, 0); + if(seek_offsets[seeks] < 0 || seek_offsets[seeks] == LONG_MAX) goto invalid_option; + seeks++; + break; + case 'n': + if(argv[i][2] == 'a') b_audio = 0; + else if(argv[i][2] == 'v') b_video = 0; + else if(argv[i][2] == 's') b_subs = 0; + else if(argv[i][2] == 'r') b_errorcode = 0; + else goto invalid_option; + break; + case 'e': + if(argv[i][2] == 'p') b_packetize = 1; + else goto invalid_option; + break; + case 'o': + if(i+1 == argc || !argv[i+1] || argv[i+1][0] == '-') goto invalid_option; + psz_out = argv[++i]; + break; + case 'p': + if(i+1 == argc || !argv[i+1]) goto invalid_option; + packets_num = strtol(argv[++i], 0, 0); + if(packets_num < 0 || packets_num == LONG_MAX) goto invalid_option; + break; + case 't': + if(i+1 == argc || !argv[i+1]) goto invalid_option; + track_num = strtol(argv[++i], 0, 0); + if(track_num == LONG_MIN || track_num == LONG_MAX) goto invalid_option; + break; + case 'h': goto usage; + default: goto invalid_option; + } + continue; + } + + /* Sanity check that we have at least an input uri */ + if(!psz_in) + { + LOG_ERROR(0, "missing uri argument"); + goto usage; + } + + return 0; + + invalid_option: + LOG_ERROR(0, "invalid command line option (%s)", argv[i]); + + usage: + psz_in = strrchr(argv[0], '\\'); if(psz_in) psz_in++; + if(!psz_in) {psz_in = strrchr(argv[0], '/'); if(psz_in) psz_in++;} + if(!psz_in) psz_in = argv[0]; + LOG_INFO(0, ""); + LOG_INFO(0, "usage: %s [options] uri", psz_in); + LOG_INFO(0, "options list:"); + LOG_INFO(0, " -i : only print information on the container"); + LOG_INFO(0, " -p X : read only X packets from the container"); + LOG_INFO(0, " -t X : read only packets from track X"); + LOG_INFO(0, " -s X : seek to X milliseconds before starting reading"); + LOG_INFO(0, " -sf X : seek forward to X milliseconds before starting reading"); + LOG_INFO(0, " -S : do seek testing"); + LOG_INFO(0, " -d : dump the data read from the container to files (-o to name file)"); + LOG_INFO(0, " -o uri: output to another uri (i.e. re-muxing)"); + LOG_INFO(0, " -na : disable audio"); + LOG_INFO(0, " -nv : disable video"); + LOG_INFO(0, " -ns : disable subtitles"); + LOG_INFO(0, " -nr : always return an error code of 0 (even in case of failure)"); + LOG_INFO(0, " -ep : enable packetization if data is not already packetized"); + LOG_INFO(0, " -c : use the client i/o functions"); + LOG_INFO(0, " -vxx : general verbosity level (replace xx with a number of \'v\')"); + LOG_INFO(0, " -vixx : verbosity specific to the input container"); + LOG_INFO(0, " -voxx : verbosity specific to the output container"); + LOG_INFO(0, " -h : help"); + return 1; +} + +static int container_test_info(VC_CONTAINER_T *ctx, bool b_reader) +{ + const char *name_type; + unsigned int i; + + LOG_INFO(0, ""); + if(b_reader) LOG_INFO(0, "----Reader Information----"); + else LOG_INFO(0, "----Writer Information----"); + + LOG_INFO(0, "duration: %2.2fs, size: %"PRId64, ctx->duration/1000000.0, ctx->size); + LOG_INFO(0, "capabilities: %x", ctx->capabilities); + LOG_INFO(0, ""); + + for(i = 0; i < ctx->tracks_num; i++) + { + VC_CONTAINER_TRACK_T *track = ctx->tracks[i]; + + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_AUDIO: name_type = "audio"; break; + case VC_CONTAINER_ES_TYPE_VIDEO: name_type = "video"; break; + case VC_CONTAINER_ES_TYPE_SUBPICTURE: name_type = "subpicture"; break; + default: name_type = "unknown"; break; + } + + LOG_INFO(0, "track: %i, type: %s, fourcc: %4.4s", i, name_type, (char *)&track->format->codec); + LOG_INFO(0, " bitrate: %i, framed: %i, enabled: %i", track->format->bitrate, + !!(track->format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED), track->is_enabled); + LOG_INFO(0, " extra data: %i, %p", track->format->extradata_size, track->format->extradata); + switch(track->format->es_type) + { + case VC_CONTAINER_ES_TYPE_AUDIO: + LOG_INFO(0, " samplerate: %i, channels: %i, bps: %i, block align: %i", + track->format->type->audio.sample_rate, track->format->type->audio.channels, + track->format->type->audio.bits_per_sample, track->format->type->audio.block_align); + LOG_INFO(0, " gapless delay: %i gapless padding: %i", + track->format->type->audio.gap_delay, track->format->type->audio.gap_padding); + LOG_INFO(0, " language: %4.4s", track->format->language); + break; + + case VC_CONTAINER_ES_TYPE_VIDEO: + LOG_INFO(0, " width: %i, height: %i, (%i,%i,%i,%i)", + track->format->type->video.width, track->format->type->video.height, + track->format->type->video.x_offset, track->format->type->video.y_offset, + track->format->type->video.visible_width, track->format->type->video.visible_height); + LOG_INFO(0, " pixel aspect ratio: %i/%i, frame rate: %i/%i", + track->format->type->video.par_num, track->format->type->video.par_den, + track->format->type->video.frame_rate_num, track->format->type->video.frame_rate_den); + break; + + case VC_CONTAINER_ES_TYPE_SUBPICTURE: + LOG_INFO(0, " language: %4.4s, encoding: %i", track->format->language, + track->format->type->subpicture.encoding); + break; + + default: break; + } + } + + for (i = 0; i < ctx->meta_num; ++i) + { + const char *name, *value; + if (i == 0) LOG_INFO(0, ""); + name = vc_container_metadata_id_to_string(ctx->meta[i]->key); + value = ctx->meta[i]->value; + if(!name) continue; + LOG_INFO(0, "metadata(%i) : %s : %s", i, name, value); + } + + LOG_INFO(0, "--------------------------"); + LOG_INFO(0, ""); + + return 0; +} + +/***************************************************************************** + * Client I/O wrapper. Only used when the right cmd line option is passed. + *****************************************************************************/ +static VC_CONTAINER_STATUS_T client_io_close( VC_CONTAINER_IO_T *p_ctx ) +{ + FILE *fd = (FILE *)p_ctx->module; + fclose(fd); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +static size_t client_io_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size) +{ + FILE *fd = (FILE *)p_ctx->module; + size_t ret = fread(buffer, 1, size, fd); + if(ret != size) + { + /* Sanity check return value. Some platforms (e.g. Android) can return -1 */ + if( ((int)ret) < 0 ) ret = 0; + + if( feof(fd) ) p_ctx->status = VC_CONTAINER_ERROR_EOS; + else p_ctx->status = VC_CONTAINER_ERROR_FAILED; + } + LOG_DEBUG( 0, "read: %i", ret ); + return ret; +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T client_io_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset) +{ + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + FILE *fd = (FILE *)p_ctx->module; + int ret; + + //FIXME: no large file support + if (offset > (int64_t)UINT_MAX) + { + LOG_ERROR( 0, "no large file support"); + p_ctx->status = VC_CONTAINER_ERROR_EOS; + return VC_CONTAINER_ERROR_EOS; + } + + ret = fseek(fd, (long)offset, SEEK_SET); + if(ret) + { + if( feof(fd) ) status = VC_CONTAINER_ERROR_EOS; + else status = VC_CONTAINER_ERROR_FAILED; + } + + p_ctx->status = status; + return status; +} + +/*****************************************************************************/ +static VC_CONTAINER_IO_T *client_io_open( const char *psz_uri, VC_CONTAINER_STATUS_T *status ) +{ + VC_CONTAINER_IO_T *p_io; + VC_CONTAINER_IO_CAPABILITIES_T capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING; + FILE *fd; + + fd = fopen(psz_uri, "rb"); + if (!fd) + { + *status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; + return NULL; + } + + p_io = vc_container_io_create( psz_uri, 0, capabilities, status ); + if(!p_io) + { + LOG_ERROR(0, "error creating io (%i)", *status); + fclose(fd); + return NULL; + } + + p_io->module = (struct VC_CONTAINER_IO_MODULE_T *)fd; + p_io->pf_close = client_io_close; + p_io->pf_read = client_io_read; + p_io->pf_seek = client_io_seek; + + //FIXME: no large file support + fseek(fd, 0, SEEK_END); + p_io->size = ftell(fd); + fseek(fd, 0, SEEK_SET); + + *status = VC_CONTAINER_SUCCESS; + return p_io; +} diff --git a/containers/test/test_bits.c b/containers/test/test_bits.c new file mode 100755 index 0000000..7ce1651 --- /dev/null +++ b/containers/test/test_bits.c @@ -0,0 +1,600 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#define BITS_LOG_INDENT(ctx) indent_level +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_bits.h" + +uint32_t indent_level; + +/** Bit stream containing the values 0 to 10, with each value in that many bits. + * At the end there is one further zero bit before the end of the stream. */ +static uint8_t bits_0_to_10[] = { + 0xCD, 0x0A, 0x30, 0x70, 0x80, 0x48, 0x14 +}; + +/** Bit stream containing the values 0 to 10, encoded using Exp-Golomb. + * At the end there is one further one bit before the end of the stream. */ +static uint8_t exp_golomb_0_to_10[] = { + 0xA6, 0x42, 0x98, 0xE2, 0x04, 0x8A, 0x17 +}; + +/** Array of signed values for the Exp-Golomb encoding of each index. */ +static int32_t exp_golomb_values[] = { + 0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5 +}; + +/** Bit stream containing two large 32-bit values, encoded using Exp-Golomb. */ +static uint8_t exp_golomb_large[] = { + 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 +}; + +/** Bit stream containing a 33-bit value, encoded using Exp-Golomb. */ +static uint8_t exp_golomb_oversize[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80 +}; + + +static const char *plural_ext(uint32_t val) +{ + return (val == 1) ? "" : "s"; +} + +static int test_reset_and_available(void) +{ + VC_CONTAINER_BITS_T bit_stream; + int error_count = 0; + + LOG_DEBUG(NULL, "Testing vc_container_bits_reset and vc_container_bits_available"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + if (!BITS_AVAILABLE(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected initialised stream to contain bits"); + error_count++; + } + + BITS_RESET(NULL, &bit_stream); + + if (BITS_AVAILABLE(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected reset stream not to contain bits"); + error_count++; + } + + return error_count; +} + +static int test_read_u32(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii, value; + int error_count = 0; + + LOG_DEBUG(NULL, "Testing vc_container_bits_get_u32"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + for (ii = 0; ii < 11; ii++) + { + value = BITS_READ_U32(NULL, &bit_stream, ii, "test_read_u32"); + if (value != ii) + { + LOG_ERROR(NULL, "Expected %u, got %u", ii, value); + error_count++; + } + } + + value = BITS_READ_U32(NULL, &bit_stream, 1, "Final bit"); + if (!BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Failed to get final bit"); + error_count++; + } + value = BITS_READ_U32(NULL, &bit_stream, 1, "Beyond final bit"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly succeeded reading beyond expected end of stream"); + error_count++; + } + + return error_count; +} + +static int test_skip(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + int error_count = 0; + uint32_t last_bits_left, bits_left; + + LOG_DEBUG(NULL, "Testing vc_container_bits_skip"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + last_bits_left = BITS_AVAILABLE(NULL, &bit_stream); + for (ii = 0; ii < 11; ii++) + { + BITS_SKIP(NULL, &bit_stream, ii, "test_skip"); + bits_left = BITS_AVAILABLE(NULL, &bit_stream); + if (bits_left + ii != last_bits_left) + { + int32_t actual = last_bits_left - bits_left; + LOG_ERROR(NULL, "Tried to skip %u bit%s, actually skipped %d bit%s", + ii, plural_ext(ii), actual, plural_ext(actual)); + error_count++; + } + last_bits_left = bits_left; + } + + BITS_SKIP(NULL, &bit_stream, 1, "Final bit"); + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Failed to skip final bit"); + error_count++; + } + if (BITS_AVAILABLE(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "End of stream not reached by skipping"); + error_count++; + } + + BITS_SKIP(NULL, &bit_stream, 1, "Beyond final bit"); + if (BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Unexpectedly succeeded skipping beyond expected end of stream"); + error_count++; + } + return error_count; +} + +static int test_ptr_and_skip_bytes(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + const uint8_t *expected_ptr; + int error_count = 0; + uint32_t last_bytes_left, bytes_left; + + LOG_DEBUG(NULL, "Testing vc_container_bits_current_pointer, vc_container_bits_skip_bytes and vc_container_bits_bytes_available"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + last_bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); + if (last_bytes_left != countof(bits_0_to_10)) + { + LOG_ERROR(NULL, "Expected bytes available to initially match given size"); + error_count++; + } + + if (BITS_CURRENT_POINTER(NULL, &bit_stream) != bits_0_to_10) + { + LOG_ERROR(NULL, "Expected initial current pointer to match original buffer"); + error_count++; + } + + expected_ptr = bits_0_to_10; + for (ii = 0; ii < 4; ii++) + { + BITS_SKIP_BYTES(NULL, &bit_stream, ii, "test_ptr_and_skip_bytes"); + + expected_ptr += ii; + if (BITS_CURRENT_POINTER(NULL, &bit_stream) != expected_ptr) + { + LOG_ERROR(NULL, "Expected current pointer to have moved by %u byte%s", ii, plural_ext(ii)); + error_count++; + } + + bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); + if (bytes_left + ii != last_bytes_left) + { + int32_t actual = last_bytes_left - bytes_left; + LOG_ERROR(NULL, "Tried to skip %u byte%s, actually skipped %d byte%s", + ii, plural_ext(ii), actual, plural_ext(actual)); + error_count++; + } + + last_bytes_left = bytes_left; + } + + if (!bytes_left) + { + LOG_ERROR(NULL, "Reached end of stream too soon"); + error_count++; + } + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected stream to be valid"); + error_count++; + } + + BITS_SKIP_BYTES(NULL, &bit_stream, bytes_left + 1, "Beyond end of stream"); + if (BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Unexpectedly succeeded skipping bytes beyond end of stream"); + error_count++; + } + if (BITS_BYTES_AVAILABLE(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected stream to have been reset"); + error_count++; + } + + return error_count; +} + +static int test_reduce_bytes(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + int error_count = 0; + uint32_t last_bytes_left, bytes_left; + + LOG_DEBUG(NULL, "Testing vc_container_bits_reduce_bytes"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + last_bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); + if (last_bytes_left != countof(bits_0_to_10)) + { + LOG_ERROR(NULL, "Expected bytes available to initially match given size"); + error_count++; + } + + if (BITS_CURRENT_POINTER(NULL, &bit_stream) != bits_0_to_10) + { + LOG_ERROR(NULL, "Expected initial current pointer to match original buffer"); + error_count++; + } + + for (ii = 0; ii < 4; ii++) + { + BITS_REDUCE_BYTES(NULL, &bit_stream, ii, "test_reduce_bytes"); + + if (BITS_CURRENT_POINTER(NULL, &bit_stream) != bits_0_to_10) + { + LOG_ERROR(NULL, "Did not expect current pointer to have moved"); + error_count++; + } + + bytes_left = BITS_BYTES_AVAILABLE(NULL, &bit_stream); + if (bytes_left + ii != last_bytes_left) + { + int32_t actual = last_bytes_left - bytes_left; + LOG_ERROR(NULL, "Tried to reduce by %u byte%s, actually reduced by %d byte%s", + ii, plural_ext(ii), actual, plural_ext(actual)); + error_count++; + } + + last_bytes_left = bytes_left; + } + + if (!bytes_left) + { + LOG_ERROR(NULL, "Reached end of stream too soon"); + error_count++; + } + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected stream to be valid"); + error_count++; + } + + BITS_REDUCE_BYTES(NULL, &bit_stream, bytes_left + 1, "Reducing an empty stream"); + if (BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Unexpectedly succeeded reducing by too many bytes"); + error_count++; + } + if (BITS_AVAILABLE(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Expected stream to have been reset"); + error_count++; + } + + return error_count; +} + +static int test_copy_bytes(void) +{ + VC_CONTAINER_BITS_T bit_stream; + int error_count = 0; + uint8_t buffer[countof(bits_0_to_10)]; + uint32_t ii; + + LOG_DEBUG(NULL, "Testing vc_container_bits_copy_bytes"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + memset(buffer, 0, sizeof(buffer)); + + /* Copy whole buffer in one go */ + BITS_COPY_BYTES(NULL, &bit_stream, countof(buffer), buffer, "Copy whole buffer"); + + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Failed to copy the whole buffer"); + error_count++; + } + + if (memcmp(buffer, bits_0_to_10, countof(bits_0_to_10)) != 0) + { + LOG_ERROR(NULL, "Single copy doesn't match original"); + error_count++; + } + + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + memset(buffer, 0, sizeof(buffer)); + + /* Copy whole buffer one byte at a time */ + for (ii = 0; ii < countof(bits_0_to_10); ii++) + { + BITS_COPY_BYTES(NULL, &bit_stream, 1, buffer + ii, "Copy buffer piecemeal"); + } + + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Failed to copy the buffer piecemeal"); + error_count++; + } + + if (memcmp(buffer, bits_0_to_10, countof(bits_0_to_10)) != 0) + { + LOG_ERROR(NULL, "Multiple copy doesn't match original"); + error_count++; + } + + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + memset(buffer, 0, sizeof(buffer)); + + /* Copy part of buffer */ + BITS_SKIP_BYTES(NULL, &bit_stream, 1, "Copy part of buffer"); + BITS_REDUCE_BYTES(NULL, &bit_stream, 1, "Copy part of buffer"); + BITS_COPY_BYTES(NULL, &bit_stream, countof(buffer) - 2, buffer, "Copy part of buffer"); + + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Failed to copy part of buffer"); + error_count++; + } + + if (memcmp(buffer, bits_0_to_10 + 1, countof(bits_0_to_10) - 2) != 0) + { + LOG_ERROR(NULL, "Partial copy doesn't match original"); + error_count++; + } + + return error_count; +} + +static int test_skip_exp_golomb(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + int error_count = 0; + + LOG_DEBUG(NULL, "Testing vc_container_bits_skip_exp_golomb"); + BITS_INIT(NULL, &bit_stream, exp_golomb_0_to_10, countof(exp_golomb_0_to_10)); + + for (ii = 0; ii < 12; ii++) + { + BITS_SKIP_EXP(NULL, &bit_stream, "test_skip_exp_golomb"); + } + + if (!BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Failed to skip through buffer"); + error_count++; + } + + BITS_SKIP_EXP(NULL, &bit_stream, "Skip beyond end of stream"); + if (BITS_VALID(NULL, &bit_stream)) + { + LOG_ERROR(NULL, "Unexpectedly succeeded skipping beyond expected end of stream"); + error_count++; + } + + return error_count; +} + +static int test_read_u32_exp_golomb(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii, value; + int error_count = 0; + + LOG_DEBUG(NULL, "Testing vc_container_bits_get_u32_exp_golomb"); + BITS_INIT(NULL, &bit_stream, exp_golomb_0_to_10, countof(exp_golomb_0_to_10)); + + for (ii = 0; ii < 11; ii++) + { + value = BITS_READ_U32_EXP(NULL, &bit_stream, "test_read_u32_exp_golomb"); + if (value != ii) + { + LOG_ERROR(NULL, "Expected %u, got %u", ii, value); + error_count++; + } + } + + value = BITS_READ_U32(NULL, &bit_stream, 1, "Final bit"); + if (!BITS_VALID(NULL, &bit_stream) || !value) + { + LOG_ERROR(NULL, "Failed to get final bit (expected a 1)"); + error_count++; + } + value = BITS_READ_U32_EXP(NULL, &bit_stream, "Beyond end of stream"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly succeeded reading beyond expected end of stream"); + error_count++; + } + + /* Test getting two large (32 bit) Exp-Golomb values */ + BITS_INIT(NULL, &bit_stream, exp_golomb_large, countof(exp_golomb_large)); + + value = BITS_READ_U32_EXP(NULL, &bit_stream, "Second largest 32-bit value"); + if (value != 0xFFFFFFFE) + { + LOG_ERROR(NULL, "Failed to get second largest 32-bit value"); + error_count++; + } + + value = BITS_READ_U32_EXP(NULL, &bit_stream, "Largest 32-bit value"); + if (value != 0xFFFFFFFF) + { + LOG_ERROR(NULL, "Failed to get largest 32-bit value"); + error_count++; + } + + /* Test getting an oversize (33 bit) Exp-Golomb value */ + BITS_INIT(NULL, &bit_stream, exp_golomb_oversize, countof(exp_golomb_oversize)); + + value = BITS_READ_U32_EXP(NULL, &bit_stream, "Unsigned 33-bit value"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly got 33-bit value: %u", value); + error_count++; + } + + return error_count; +} + +static int test_read_s32_exp_golomb(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + int32_t value; + int error_count = 0; + + LOG_DEBUG(NULL, "Testing vc_container_bits_get_s32_exp_golomb"); + BITS_INIT(NULL, &bit_stream, exp_golomb_0_to_10, countof(exp_golomb_0_to_10)); + + for (ii = 0; ii < 11; ii++) + { + value = BITS_READ_S32_EXP(NULL, &bit_stream, "test_read_s32_exp_golomb"); + if (value != exp_golomb_values[ii]) + { + LOG_ERROR(NULL, "Expected %u, got %u", ii, value); + error_count++; + } + } + + value = BITS_READ_S32_EXP(NULL, &bit_stream, "Final bit"); + if (!BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Failed to get final Exp-Golomb value (expected a zero)"); + error_count++; + } + value = BITS_READ_S32_EXP(NULL, &bit_stream, "Beyond final bit"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly succeeded reading beyond expected end of stream"); + error_count++; + } + + /* Test getting two large (32 bit) Exp-Golomb values */ + BITS_INIT(NULL, &bit_stream, exp_golomb_large, countof(exp_golomb_large)); + + value = BITS_READ_S32_EXP(NULL, &bit_stream, "Largest signed 32-bit value"); + if (!BITS_VALID(NULL, &bit_stream) || value != -0x7FFFFFFF) + { + LOG_ERROR(NULL, "Failed to get largest signed 32-bit value: %d", value); + error_count++; + } + + value = BITS_READ_S32_EXP(NULL, &bit_stream, "Just too large signed 33-bit value"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly got slightly too large signed 32-bit value: %d", value); + error_count++; + } + + /* Test getting an oversize (33 bit) Exp-Golomb value */ + BITS_INIT(NULL, &bit_stream, exp_golomb_oversize, countof(exp_golomb_oversize)); + + value = BITS_READ_S32_EXP(NULL, &bit_stream, "Larger signed 33-bit value"); + if (BITS_VALID(NULL, &bit_stream) || value) + { + LOG_ERROR(NULL, "Unexpectedly got signed 33-bit value: %d", value); + error_count++; + } + + return error_count; +} + +#ifdef ENABLE_CONTAINERS_LOG_FORMAT +static int test_indentation(void) +{ + VC_CONTAINER_BITS_T bit_stream; + uint32_t ii; + + LOG_DEBUG(NULL, "Testing logging indentation"); + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + for (ii = 0; ii < 11; ii++) + { + indent_level = ii; + BITS_READ_U32(NULL, &bit_stream, ii, "test_indentation (unit)"); + } + + BITS_INIT(NULL, &bit_stream, bits_0_to_10, countof(bits_0_to_10)); + + for (ii = 0; ii < 11; ii++) + { + indent_level = ii * 10; + BITS_READ_U32(NULL, &bit_stream, ii, "test_indentation (tens)"); + } + return 0; +} +#endif + +int main(int argc, char **argv) +{ + int error_count = 0; + + VC_CONTAINER_PARAM_UNUSED(argc); + VC_CONTAINER_PARAM_UNUSED(argv); + + error_count += test_reset_and_available(); + error_count += test_read_u32(); + error_count += test_skip(); + error_count += test_ptr_and_skip_bytes(); + error_count += test_reduce_bytes(); + error_count += test_copy_bytes(); + error_count += test_skip_exp_golomb(); + error_count += test_read_u32_exp_golomb(); + error_count += test_read_s32_exp_golomb(); +#ifdef ENABLE_CONTAINERS_LOG_FORMAT + error_count += test_indentation(); +#endif + + if (error_count) + { + LOG_ERROR(NULL, "*** %d errors reported", error_count); + getchar(); + } + + return error_count; +} diff --git a/containers/test/test_uri.c b/containers/test/test_uri.c new file mode 100755 index 0000000..0586182 --- /dev/null +++ b/containers/test/test_uri.c @@ -0,0 +1,824 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "containers/containers.h" +#include "containers/core/containers_common.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_uri.h" + +#define TEST_CHAR '.' +#define TEST_STRING "test" +#define TEST_NAME "name" +#define TEST_VALUE "value" + +#define ARRAY_SIZE(X) (sizeof(X)/sizeof(*(X))) + +struct +{ + const char *before; + const char *after; +} test_parse_uris[] = { + {"", NULL}, + {"C:\\test\\filename", NULL}, + {"/usr/local/nix/filename", NULL}, + {"scheme-only:", NULL}, + {"scheme:/and/path", NULL}, + {"scheme:/and/path#with_fragment", NULL}, + {"scheme:/and/path?query=true", NULL}, + {"scheme:/and/path?multiple+queries=true&more=false", NULL}, + {"scheme:/and/path?multiple+queries=true&more=false#and+a+fragment,+too", NULL}, + {"scheme:C:\\Windows\\path", "scheme:C:%5CWindows%5Cpath"}, + {"scheme:C:\\Windows\\path#with_fragment", "scheme:C:%5CWindows%5Cpath#with_fragment"}, + {"scheme:C:\\Windows\\path?query=true", "scheme:C:%5CWindows%5Cpath?query=true"}, + {"scheme:C:\\Windows\\path?query#and+fragment", "scheme:C:%5CWindows%5Cpath?query#and+fragment"}, + {"scheme://just.host", NULL}, + {"scheme://host/and/path", NULL}, + {"scheme://host:port", NULL}, + {"scheme://host:port/and/path", NULL}, + {"scheme://127.0.0.1:port/and/path", NULL}, + {"scheme://userinfo@host:port/and/path?query#fragment", NULL}, + {"HTTP://www.EXAMPLE.com/", "http://www.example.com/"}, + {"%48%54%54%50://%54%45%53%54/", "http://test/"}, + {"scheme:esc%", "scheme:esc%25"}, + {"scheme:esc%%", "scheme:esc%25%25"}, + {"scheme:esc%%%", "scheme:esc"}, + {"scheme:esc%1%", "scheme:esc%10"}, + {"scheme:esc%%1", "scheme:esc%01"}, + {"s+-.1234567890:", NULL}, + {"scheme:///", NULL}, + {"scheme://:just_port", NULL}, + {"scheme://:port/and/path", NULL}, + {"scheme://just_userinfo@", NULL}, + {"scheme://userinfo@/and/path", NULL}, + {"scheme://userinfo@:port/and/path", NULL}, + {"%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0f%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f:", "%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F:"}, + {"%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F:", "%20%21%22%23%24%25%26%27%28%29%2A+%2C-.%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~%7F:"}, + {"scheme://%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F@", "scheme://%20!%22%23$%25&'()*+,-.%2F:;%3C=%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~%7F@"}, + {"scheme://%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://%20!%22%23$%25&'()*+,-.%2F:;%3C=%3E%3F%40[%5C]%5E_%60%7B%7C%7D~%7F"}, + {"scheme://:%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://:%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, + {"scheme:///%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme:///%20!%22%23$%25&'()*+,-./:;%3C=%3E%3F@%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, + {"scheme://?%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://?%20!%22%23$%25&'()*+,-./:;%3C=%3E?@%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, + {"scheme://#%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%5F%60%7B%7C%7D%7E%7F", "scheme://#%20!%22%23$%25&'()*+,-./:;%3C=%3E?@%5B%5C%5D%5E_%60%7B%7C%7D~%7F"}, + {"scheme://[v6.1234:5678]/", NULL}, + {"scheme://[::1]/", NULL}, + {"scheme://[1234:5678:90ab:cdef:1234:5678:90ab:cdef]/", NULL}, + {"scheme://[::1]:1/", NULL}, + {"scheme://?", "scheme://"}, + {"scheme://?#", "scheme://#"}, + {"scheme://?q&&;&n=&=v&=&n=v", "scheme://?q&n=&n=v"}, + {"ldap://[2001:db8::7]/c=GB?objectClass?one", NULL}, + {"mailto:John.Doe@example.com", NULL}, + {"news:comp.infosystems.www.servers.unix", NULL}, + {"tel:+1-816-555-1212", NULL}, + {"urn:oasis:names:specification:docbook:dtd:xml:4.1.2", NULL}, +}; + +typedef struct build_uri_tag +{ + const char *expected_uri; + const char *scheme; + const char *userinfo; + const char *host; + const char *port; + const char *path; + const char *fragment; + const char **queries; /* Held as name then value. NULL values allowed. NULL name terminates list */ +} BUILD_URI_T; + +/* Test query lists */ +const char *no_queries[] = { NULL }; +const char *query_true[] = { "query", "true", NULL }; +const char *multiple_queries[] = { "multiple+queries", "true", "more", "false", NULL }; +const char *just_query[] = {"query", NULL, NULL}; +const char *complex_query[] = { "q", NULL, "n", "", "n", "v", NULL }; +const char *objectClass_one[] = { "objectClass?one", NULL, NULL }; + +BUILD_URI_T test_build_uris[] = { + {"", NULL, NULL, NULL, NULL, NULL, NULL, no_queries }, + {"C:\\test\\filename", NULL, NULL, NULL, NULL, "C:\\test\\filename", NULL, no_queries}, + {"/usr/local/nix/filename", NULL, NULL, NULL, NULL, "/usr/local/nix/filename", NULL, no_queries}, + {"scheme-only:", "scheme-only", NULL, NULL, NULL, NULL, NULL, no_queries}, + {"scheme:/and/path", "scheme", NULL, NULL, NULL, "/and/path", NULL, no_queries}, + {"scheme:/and/path#with_fragment", "scheme", NULL, NULL, NULL, "/and/path", "with_fragment", no_queries}, + {"scheme:/and/path?query=true", "scheme", NULL, NULL, NULL, "/and/path", NULL, query_true}, + {"scheme:/and/path?multiple+queries=true&more=false", "scheme", NULL, NULL, NULL, "/and/path", NULL, multiple_queries}, + {"scheme:/and/path?multiple+queries=true&more=false#and+a+fragment,+too", "scheme", NULL, NULL, NULL, "/and/path", "and+a+fragment,+too", multiple_queries}, + {"scheme://just.host", "scheme", NULL, "just.host", NULL, NULL, NULL, no_queries}, + {"scheme://host/and/path", "scheme", NULL, "host", NULL, "/and/path", NULL, no_queries}, + {"scheme://host:port", "scheme", NULL, "host", "port", NULL, NULL, no_queries}, + {"scheme://host:port/and/path", "scheme", NULL, "host", "port", "/and/path", NULL, no_queries}, + {"scheme://127.0.0.1:port/and/path", "scheme", NULL, "127.0.0.1", "port", "/and/path", NULL, no_queries}, + {"scheme://userinfo@host:port/and/path?query#fragment", "scheme", "userinfo", "host", "port", "/and/path", "fragment", just_query}, + {"scheme:///", "scheme", NULL, "", NULL, "/", NULL, no_queries }, + {"scheme://:just_port", "scheme", NULL, "", "just_port", NULL, NULL, no_queries }, + {"scheme://:port/and/path", "scheme", NULL, "", "port", "/and/path", NULL, no_queries }, + {"scheme://just_userinfo@", "scheme", "just_userinfo", "", NULL, NULL, NULL, no_queries }, + {"scheme://userinfo@/and/path", "scheme", "userinfo", "", NULL, "/and/path", NULL, no_queries }, + {"scheme://userinfo@:port/and/path", "scheme", "userinfo", "", "port", "/and/path", NULL, no_queries }, + {"scheme://", "scheme", NULL, "", NULL, NULL, NULL, no_queries }, + {"scheme://#", "scheme", NULL, "", NULL, NULL, "", no_queries }, + {"scheme://?q&n=&n=v", "scheme", NULL, "", NULL, NULL, NULL, complex_query}, + {"ldap://[2001:db8::7]/c=GB?objectClass?one", "ldap", NULL, "[2001:db8::7]", NULL, "/c=GB", NULL, objectClass_one}, + {"mailto:John.Doe@example.com", "mailto", NULL, NULL, NULL, "John.Doe@example.com", NULL, no_queries }, + {"news:comp.infosystems.www.servers.unix", "news", NULL, NULL, NULL, "comp.infosystems.www.servers.unix", NULL, no_queries }, + {"tel:+1-816-555-1212", "tel", NULL, NULL, NULL, "+1-816-555-1212", NULL, no_queries }, + {"urn:oasis:names:specification:docbook:dtd:xml:4.1.2", "urn", NULL, NULL, NULL, "oasis:names:specification:docbook:dtd:xml:4.1.2", NULL, no_queries }, +}; + +typedef struct merge_uri_tag +{ + const char *base; + const char *relative; + const char *merged; +} MERGE_URI_T; + +MERGE_URI_T test_merge_uris[] = { + /* Normal examples */ + { "http://a/b/c/d;p?q#f", "ftp:h", "ftp:h" }, + { "http://a/b/c/d;p?q#f", "g", "http://a/b/c/g" }, + { "http://a/b/c/d;p?q#f", "./g", "http://a/b/c/g" }, + { "http://a/b/c/d;p?q#f", "g/", "http://a/b/c/g/" }, + { "http://a/b/c/d;p?q#f", "/g", "http://a/g" }, + { "http://a/b/c/d;p?q#f", "//g", "http://g" }, + { "http://a/b/c/d;p?q#f", "?y", "http://a/b/c/d;p?y" }, + { "http://a/b/c/d;p?q#f", "g?y", "http://a/b/c/g?y" }, + { "http://a/b/c/d;p?q#f", "g?y/./x", "http://a/b/c/g?y/./x" }, + { "http://a/b/c/d;p?q#f", "#s", "http://a/b/c/d;p?q#s" }, + { "http://a/b/c/d;p?q#f", "g#s", "http://a/b/c/g#s" }, + { "http://a/b/c/d;p?q#f", "g#s/./x", "http://a/b/c/g#s/./x" }, + { "http://a/b/c/d;p?q#f", "g?y#s", "http://a/b/c/g?y#s" }, + { "http://a/b/c/d;p?q#f", ";x", "http://a/b/c/d;x" }, + { "http://a/b/c/d;p?q#f", "g;x", "http://a/b/c/g;x" }, + { "http://a/b/c/d;p?q#f", "g;x?y#s", "http://a/b/c/g;x?y#s" }, + { "http://a/b/c/d;p?q#f", ".", "http://a/b/c/" }, + { "http://a/b/c/d;p?q#f", "./", "http://a/b/c/" }, + { "http://a/b/c/d;p?q#f", "..", "http://a/b/" }, + { "http://a/b/c/d;p?q#f", "../", "http://a/b/" }, + { "http://a/b/c/d;p?q#f", "../g", "http://a/b/g" }, + { "http://a/b/c/d;p?q#f", "../..", "http://a/" }, + { "http://a/b/c/d;p?q#f", "../../", "http://a/" }, + { "http://a/b/c/d;p?q#f", "../../g", "http://a/g" }, + /* Normal examples, without base network info */ + { "http:/b/c/d;p?q#f", "g", "http:/b/c/g" }, + { "http:/b/c/d;p?q#f", "./g", "http:/b/c/g" }, + { "http:/b/c/d;p?q#f", "g/", "http:/b/c/g/" }, + { "http:/b/c/d;p?q#f", "/g", "http:/g" }, + { "http:/b/c/d;p?q#f", "//g", "http://g" }, + { "http:/b/c/d;p?q#f", "?y", "http:/b/c/d;p?y" }, + { "http:/b/c/d;p?q#f", "g?y", "http:/b/c/g?y" }, + { "http:/b/c/d;p?q#f", "g?y/./x", "http:/b/c/g?y/./x" }, + { "http:/b/c/d;p?q#f", "#s", "http:/b/c/d;p?q#s" }, + { "http:/b/c/d;p?q#f", "g#s", "http:/b/c/g#s" }, + { "http:/b/c/d;p?q#f", "g#s/./x", "http:/b/c/g#s/./x" }, + { "http:/b/c/d;p?q#f", "g?y#s", "http:/b/c/g?y#s" }, + { "http:/b/c/d;p?q#f", ";x", "http:/b/c/d;x" }, + { "http:/b/c/d;p?q#f", "g;x", "http:/b/c/g;x" }, + { "http:/b/c/d;p?q#f", "g;x?y#s", "http:/b/c/g;x?y#s" }, + { "http:/b/c/d;p?q#f", ".", "http:/b/c/" }, + { "http:/b/c/d;p?q#f", "./", "http:/b/c/" }, + { "http:/b/c/d;p?q#f", "..", "http:/b/" }, + { "http:/b/c/d;p?q#f", "../", "http:/b/" }, + { "http:/b/c/d;p?q#f", "../g", "http:/b/g" }, + { "http:/b/c/d;p?q#f", "../..", "http:/" }, + { "http:/b/c/d;p?q#f", "../../", "http:/" }, + { "http:/b/c/d;p?q#f", "../../g", "http:/g" }, + /* Normal examples, without base network info or path root */ + { "http:b/c/d;p?q#f", "g", "http:b/c/g" }, + { "http:b/c/d;p?q#f", "./g", "http:b/c/g" }, + { "http:b/c/d;p?q#f", "g/", "http:b/c/g/" }, + { "http:b/c/d;p?q#f", "/g", "http:/g" }, + { "http:b/c/d;p?q#f", "//g", "http://g" }, + { "http:b/c/d;p?q#f", "?y", "http:b/c/d;p?y" }, + { "http:b/c/d;p?q#f", "g?y", "http:b/c/g?y" }, + { "http:b/c/d;p?q#f", "g?y/./x", "http:b/c/g?y/./x" }, + { "http:b/c/d;p?q#f", "#s", "http:b/c/d;p?q#s" }, + { "http:b/c/d;p?q#f", "g#s", "http:b/c/g#s" }, + { "http:b/c/d;p?q#f", "g#s/./x", "http:b/c/g#s/./x" }, + { "http:b/c/d;p?q#f", "g?y#s", "http:b/c/g?y#s" }, + { "http:b/c/d;p?q#f", ";x", "http:b/c/d;x" }, + { "http:b/c/d;p?q#f", "g;x", "http:b/c/g;x" }, + { "http:b/c/d;p?q#f", "g;x?y#s", "http:b/c/g;x?y#s" }, + { "http:b/c/d;p?q#f", ".", "http:b/c/" }, + { "http:b/c/d;p?q#f", "./", "http:b/c/" }, + { "http:b/c/d;p?q#f", "..", "http:b/" }, + { "http:b/c/d;p?q#f", "../", "http:b/" }, + { "http:b/c/d;p?q#f", "../g", "http:b/g" }, + { "http:b/c/d;p?q#f", "../..", "http:" }, + { "http:b/c/d;p?q#f", "../../", "http:" }, + { "http:b/c/d;p?q#f", "../../g", "http:g" }, + /* Normal examples, without base path */ + { "http://a?q#f", "g", "http://a/g" }, + { "http://a?q#f", "./g", "http://a/g" }, + { "http://a?q#f", "g/", "http://a/g/" }, + { "http://a?q#f", "/g", "http://a/g" }, + { "http://a?q#f", "//g", "http://g" }, + { "http://a?q#f", "?y", "http://a?y" }, + { "http://a?q#f", "g?y", "http://a/g?y" }, + { "http://a?q#f", "g?y/./x", "http://a/g?y/./x" }, + { "http://a?q#f", "#s", "http://a?q#s" }, + { "http://a?q#f", "g#s", "http://a/g#s" }, + { "http://a?q#f", "g#s/./x", "http://a/g#s/./x" }, + { "http://a?q#f", "g?y#s", "http://a/g?y#s" }, + { "http://a?q#f", ";x", "http://a/;x" }, + { "http://a?q#f", "g;x", "http://a/g;x" }, + { "http://a?q#f", "g;x?y#s", "http://a/g;x?y#s" }, + { "http://a?q#f", ".", "http://a/" }, + { "http://a?q#f", "./", "http://a/" }, + /* Normal examples, without base network info or path */ + { "http:?q#f", "g", "http:g" }, + { "http:?q#f", "./g", "http:g" }, + { "http:?q#f", "g/", "http:g/" }, + { "http:?q#f", "/g", "http:/g" }, + { "http:?q#f", "//g", "http://g" }, + { "http:?q#f", "?y", "http:?y" }, + { "http:?q#f", "g?y", "http:g?y" }, + { "http:?q#f", "g?y/./x", "http:g?y/./x" }, + { "http:?q#f", "#s", "http:?q#s" }, + { "http:?q#f", "g#s", "http:g#s" }, + { "http:?q#f", "g#s/./x", "http:g#s/./x" }, + { "http:?q#f", "g?y#s", "http:g?y#s" }, + { "http:?q#f", ";x", "http:;x" }, + { "http:?q#f", "g;x", "http:g;x" }, + { "http:?q#f", "g;x?y#s", "http:g;x?y#s" }, + /* Abnormal (but valid) examples */ + { "http://a/b/c/d;p?q#f", "../../../g", "http://a/../g" }, + { "http://a/b/c/d;p?q#f", "../../../../g", "http://a/../../g" }, + { "http://a/b/c/d;p?q#f", "/./g", "http://a/./g" }, + { "http://a/b/c/d;p?q#f", "/../g", "http://a/../g" }, + { "http://a/b/c/d;p?q#f", "g.", "http://a/b/c/g." }, + { "http://a/b/c/d;p?q#f", ".g", "http://a/b/c/.g" }, + { "http://a/b/c/d;p?q#f", "g..", "http://a/b/c/g.." }, + { "http://a/b/c/d;p?q#f", "..g", "http://a/b/c/..g" }, + { "http://a/b/c/d;p?q#f", "./../g", "http://a/b/g" }, + { "http://a/b/c/d;p?q#f", "./g/.", "http://a/b/c/g/" }, + { "http://a/b/c/d;p?q#f", "g/./h", "http://a/b/c/g/h" }, + { "http://a/b/c/d;p?q#f", "g/../h", "http://a/b/c/h" }, + { "http://a/b/c/d;p?q#f", "./g:h", "http://a/b/c/g:h" }, + { "http://a/b/c/d;p?q#f", "g/..", "http://a/b/c/" }, + /* Abnormal examples without base path */ + { "http://a?q#f", "../g", "http://a/../g" }, + { "http://a?q#f", "./../g", "http://a/../g" }, + /* Abnormal examples without base network info or path */ + { "http:?q#f", "../g", "http:../g" }, + { "http:?q#f", "./../g", "http:../g" }, + { "http:?q#f", ".", "http:" }, + { "http:?q#f", "./", "http:" }, +}; + + +/** Dump a URI structure to the log. + * + * \param uri URI structure to be dumped. */ +static void dump_uri(VC_URI_PARTS_T *uri) +{ + const char *str; + uint32_t query_count, ii; + + str = vc_uri_scheme(uri); + if (str) + LOG_DEBUG(NULL, "Scheme: <%s>", str); + + str = vc_uri_userinfo(uri); + if (str) + LOG_DEBUG(NULL, "Userinfo: <%s>", str); + + str = vc_uri_host(uri); + if (str) + LOG_DEBUG(NULL, "Host: <%s>", str); + + str = vc_uri_port(uri); + if (str) + LOG_DEBUG(NULL, "Port: <%s>", str); + + str = vc_uri_path(uri); + if (str) + LOG_DEBUG(NULL, "Path: <%s>", str); + + query_count = vc_uri_num_queries(uri); + for (ii = 0; ii < query_count; ii++) + { + const char *value; + + vc_uri_query(uri, ii, &str, &value); + if (str) + { + if (value) + LOG_DEBUG(NULL, "Query %d: <%s>=<%s>", ii, str, value); + else + LOG_DEBUG(NULL, "Query %d: <%s>", ii, str); + } + } + + str = vc_uri_fragment(uri); + if (str) + LOG_DEBUG(NULL, "Fragment: <%s>", str); +} + +static int check_uri(VC_URI_PARTS_T *uri, const char *expected) +{ + uint32_t built_len; + char *built; + + built_len = vc_uri_build(uri, NULL, 0) + 1; + built = (char *)malloc(built_len); + if (!built) + { + LOG_ERROR(NULL, "*** Unexpected memory allocation failure: %d bytes", built_len); + return 1; + } + + vc_uri_build(uri, built, built_len); + + if (strcmp(built, expected) != 0) + { + LOG_ERROR(NULL, "*** Built URI <%s>\nexpected <%s>", built, expected); + free(built); + return 1; + } + + free(built); + + return 0; +} + +static int check_null_uri_pointer(void) +{ + int error_count = 0; + char buffer[1]; + + /* Check NULL URI can be passed without failure to all routines */ + vc_uri_release( NULL ); + vc_uri_clear( NULL ); + if (vc_uri_parse( NULL, NULL )) + error_count++; + if (vc_uri_parse( NULL, "" )) + error_count++; + if (vc_uri_build( NULL, NULL, 0 ) != 0) + error_count++; + buffer[0] = TEST_CHAR; + if (vc_uri_build( NULL, buffer, sizeof(buffer) ) != 0) + error_count++; + if (buffer[0] != TEST_CHAR) + error_count++; + if (vc_uri_scheme( NULL )) + error_count++; + if (vc_uri_userinfo( NULL )) + error_count++; + if (vc_uri_host( NULL )) + error_count++; + if (vc_uri_port( NULL )) + error_count++; + if (vc_uri_path( NULL )) + error_count++; + if (vc_uri_fragment( NULL )) + error_count++; + if (vc_uri_num_queries( NULL ) != 0) + error_count++; + vc_uri_query( NULL, 0, NULL, NULL ); + if (vc_uri_set_scheme( NULL, NULL )) + error_count++; + if (vc_uri_set_userinfo( NULL, NULL )) + error_count++; + if (vc_uri_set_host( NULL, NULL )) + error_count++; + if (vc_uri_set_port( NULL, NULL )) + error_count++; + if (vc_uri_set_path( NULL, NULL )) + error_count++; + if (vc_uri_set_fragment( NULL, NULL )) + error_count++; + if (vc_uri_add_query( NULL, NULL, NULL )) + error_count++; + + if (error_count) + LOG_ERROR(NULL, "NULL URI parameter testing failed"); + + return error_count; +} + +static int check_parse_parameters(VC_URI_PARTS_T *uri) +{ + int error_count = 0; + + if (vc_uri_parse( uri, NULL )) + { + LOG_ERROR(NULL, "Parsing NULL URI failed"); + error_count++; + } + + return error_count; +} + +static int check_build_parameters(VC_URI_PARTS_T *uri) +{ + int error_count = 0; + char buffer[1]; + + vc_uri_set_path( uri, TEST_STRING ); + + if (vc_uri_build( uri, NULL, 0 ) != strlen(TEST_STRING)) + { + LOG_ERROR(NULL, "Retrieving URI length failed"); + error_count++; + } + + buffer[0] = TEST_CHAR; + if (vc_uri_build( uri, buffer, 1 ) != strlen(TEST_STRING)) + { + LOG_ERROR(NULL, "Building URI to small buffer failed"); + error_count++; + } + if (buffer[0] != TEST_CHAR) + { + LOG_ERROR(NULL, "Building URI to small buffer modified buffer"); + error_count++; + } + + vc_uri_set_path( uri, NULL ); /* Reset uri */ + + return error_count; +} + +static int check_get_defaults(VC_URI_PARTS_T *uri) +{ + int error_count = 0; + const char *name = NULL, *value = NULL; + char buffer[1]; + + if (vc_uri_scheme( uri )) + error_count++; + if (vc_uri_userinfo( uri )) + error_count++; + if (vc_uri_host( uri )) + error_count++; + if (vc_uri_port( uri )) + error_count++; + if (vc_uri_path( uri )) + error_count++; + if (vc_uri_fragment( uri )) + error_count++; + if (vc_uri_num_queries( uri ) != 0) + error_count++; + + vc_uri_query( uri, 0, &name, &value ); + if (name != NULL || value != NULL) + error_count++; + + if (vc_uri_build(uri, NULL, 0) != 0) + error_count++; + buffer[0] = ~*TEST_STRING; /* Initialize with something */ + vc_uri_build(uri, buffer, sizeof(buffer)); + if (buffer[0] != '\0') /* Expect empty string */ + error_count++; + + if (error_count) + LOG_ERROR(NULL, "Getting default values gave unexpected values"); + + return error_count; +} + +static int check_accessors(VC_URI_PARTS_T *uri) +{ + int error_count = 0; + const char *str; + + if (vc_uri_set_scheme( uri, TEST_STRING )) + { + str = vc_uri_scheme(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_scheme( uri, NULL )) + error_count++; + if (vc_uri_scheme(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_set_userinfo( uri, TEST_STRING )) + { + str = vc_uri_userinfo(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_userinfo( uri, NULL )) + error_count++; + if (vc_uri_userinfo(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_set_host( uri, TEST_STRING )) + { + str = vc_uri_host(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_host( uri, NULL )) + error_count++; + if (vc_uri_host(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_set_port( uri, TEST_STRING )) + { + str = vc_uri_port(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_port( uri, NULL )) + error_count++; + if (vc_uri_port(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_set_path( uri, TEST_STRING )) + { + str = vc_uri_path(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_path( uri, NULL )) + error_count++; + if (vc_uri_path(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_set_fragment( uri, TEST_STRING )) + { + str = vc_uri_fragment(uri); + if (!str || strcmp(TEST_STRING, str)) + error_count++; + if (!vc_uri_set_fragment( uri, NULL )) + error_count++; + if (vc_uri_fragment(uri)) + error_count++; + } else + error_count++; + + if (vc_uri_add_query( uri, NULL, NULL )) + error_count++; + if (vc_uri_add_query( uri, NULL, TEST_VALUE )) + error_count++; + if (!vc_uri_add_query( uri, TEST_STRING, NULL )) + error_count++; + if (!vc_uri_add_query( uri, TEST_NAME, TEST_VALUE )) + error_count++; + + if (vc_uri_num_queries(uri) == 2) + { + const char *name = NULL, *value = NULL; + + vc_uri_query(uri, 0, &name, &value); + if (!name || strcmp(TEST_STRING, name)) + error_count++; + if (value) + error_count++; + + vc_uri_query(uri, 1, &name, &value); + if (!name || strcmp(TEST_NAME, name)) + error_count++; + if (!value || strcmp(TEST_VALUE, value)) + error_count++; + } else + error_count++; + + if (error_count) + LOG_ERROR(NULL, "Accessors failed"); + + return error_count; +} + +/** Test parameter validation + * + * \param uri Pre-created URI structure. + * \return 1 on error, 0 on success. */ +static int test_parameter_validation(VC_URI_PARTS_T *uri) +{ + int error_count = 0; + + error_count += check_null_uri_pointer(); + error_count += check_get_defaults(uri); + error_count += check_parse_parameters(uri); + error_count += check_build_parameters(uri); + error_count += check_accessors(uri); + + return error_count; +} + +/** Test parsing and rebuilding a URI. + * + * \param uri Pre-created URI structure. + * \param original The original URI string. + * \param expected The expected, re-built string, or NULL if original is expected. + * \return 1 on error, 0 on success. */ +static int test_parsing_uri(VC_URI_PARTS_T *uri, const char *original, const char *expected) +{ + bool parsed; + + LOG_INFO(NULL, "URI: <%s>", original); + + parsed = vc_uri_parse( uri, original ); + if (!parsed) + { + LOG_ERROR(NULL, "*** Expected <%s> to parse, but it didn't", original); + return 1; + } + + dump_uri(uri); + + return check_uri(uri, expected ? expected : original); +} + +/** Test building a URI from component parts. + * + * \param uri Pre-created URI structure. + * \param uri_data The data for building the URI and the expected output. + * \return 1 on error, 0 on success. */ +static int test_building_uri(VC_URI_PARTS_T *uri, BUILD_URI_T *uri_data) +{ + const char **p_str; + const char *name, *value; + + LOG_INFO(NULL, "Building URI <%s>", uri_data->expected_uri); + + vc_uri_clear(uri); + + if (!vc_uri_set_scheme(uri, uri_data->scheme)) + { + LOG_ERROR(NULL, "*** Failed to set scheme"); + return 1; + } + + if (!vc_uri_set_userinfo(uri, uri_data->userinfo)) + { + LOG_ERROR(NULL, "*** Failed to set userinfo"); + return 1; + } + + if (!vc_uri_set_host(uri, uri_data->host)) + { + LOG_ERROR(NULL, "*** Failed to set host"); + return 1; + } + + if (!vc_uri_set_port(uri, uri_data->port)) + { + LOG_ERROR(NULL, "*** Failed to set port"); + return 1; + } + + if (!vc_uri_set_path(uri, uri_data->path)) + { + LOG_ERROR(NULL, "*** Failed to set path"); + return 1; + } + + if (!vc_uri_set_fragment(uri, uri_data->fragment)) + { + LOG_ERROR(NULL, "*** Failed to set fragment"); + return 1; + } + + p_str = uri_data->queries; + name = *p_str++; + + while (name) + { + value = *p_str++; + if (!vc_uri_add_query(uri, name, value)) + { + LOG_ERROR(NULL, "*** Failed to add query"); + return 1; + } + name = *p_str++; + } + + dump_uri(uri); + + return check_uri(uri, uri_data->expected_uri); +} + +/** Test merging a relative URI with a base URI. + * + * \param uri Pre-created URI structure. + * \param uri_data The nase and relative URIs and the expected output. + * \return 1 on error, 0 on success. */ +static int test_merging_uri(VC_URI_PARTS_T *uri, MERGE_URI_T *uri_data) +{ + VC_URI_PARTS_T *base_uri; + + LOG_INFO(NULL, "Base <%s>, relative <%s>, expect <%s>", uri_data->base, uri_data->relative, uri_data->merged); + + vc_uri_clear(uri); + base_uri = vc_uri_create(); + if (!base_uri) + { + LOG_ERROR(NULL, "*** Failed to allocate base URI structure"); + return 1; + } + + if (!vc_uri_parse(base_uri, uri_data->base)) + { + LOG_ERROR(NULL, "*** Failed to parse base URI structure"); + return 1; + } + if (!vc_uri_parse(uri, uri_data->relative)) + { + LOG_ERROR(NULL, "*** Failed to parse relative URI structure"); + return 1; + } + + if (!vc_uri_merge(base_uri, uri)) + { + LOG_ERROR(NULL, "*** Failed to merge base and relative URIs"); + return 1; + } + + vc_uri_release(base_uri); + + return check_uri(uri, uri_data->merged); +} + +int main(int argc, char **argv) +{ + VC_URI_PARTS_T *uri; + int error_count = 0; + size_t ii; + + uri = vc_uri_create(); + if (!uri) + { + LOG_ERROR(NULL, "*** Failed to create URI structure."); + return 1; + } + + LOG_INFO(NULL, "Test parameter validation"); + error_count += test_parameter_validation(uri); + + LOG_INFO(NULL, "Test parsing URIs:"); + for (ii = 0; ii < ARRAY_SIZE(test_parse_uris); ii++) + { + error_count += test_parsing_uri(uri, test_parse_uris[ii].before, test_parse_uris[ii].after); + } + + LOG_INFO(NULL, "Test building URIs:"); + for (ii = 0; ii < ARRAY_SIZE(test_build_uris); ii++) + { + error_count += test_building_uri(uri, &test_build_uris[ii]); + } + + LOG_INFO(NULL, "Test merging URIs:"); + for (ii = 0; ii < ARRAY_SIZE(test_merge_uris); ii++) + { + error_count += test_merging_uri(uri, &test_merge_uris[ii]); + } + + if (argc > 1) + { + LOG_INFO(NULL, "Test parsing URIs from command line:"); + + while (argc-- > 1) + { + /* Test URIs passed on the command line are expected to parse and to + * match themselves when rebuilt. */ + error_count += test_parsing_uri(uri, argv[argc], NULL); + } + } + + vc_uri_release(uri); + + if (error_count) + LOG_ERROR(NULL, "*** %d errors reported", error_count); + +#ifdef _MSC_VER + LOG_INFO(NULL, "Press return to complete test."); + getchar(); +#endif + + return error_count; +} diff --git a/containers/test/uri_pipe.c b/containers/test/uri_pipe.c new file mode 100755 index 0000000..6dd6072 --- /dev/null +++ b/containers/test/uri_pipe.c @@ -0,0 +1,116 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "containers/containers.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_io.h" + +#include "nb_io.h" + +#define MAX_BUFFER_SIZE 2048 + +int main(int argc, char **argv) +{ + char buffer[MAX_BUFFER_SIZE]; + VC_CONTAINER_IO_T *read_io, *write_io; + VC_CONTAINER_STATUS_T status; + size_t received; + bool ready = true; + + if (argc < 3) + { + LOG_INFO(NULL, "Usage:\n%s \n", argv[0]); + return 1; + } + + read_io = vc_container_io_open(argv[1], VC_CONTAINER_IO_MODE_READ, &status); + if (!read_io) + { + LOG_INFO(NULL, "Opening <%s> for read failed: %d\n", argv[1], status); + return 2; + } + + write_io = vc_container_io_open(argv[2], VC_CONTAINER_IO_MODE_WRITE, &status); + if (!write_io) + { + vc_container_io_close(read_io); + LOG_INFO(NULL, "Opening <%s> for write failed: %d\n", argv[2], status); + return 3; + } + + nb_set_nonblocking_input(1); + + while (ready) + { + size_t total_written = 0; + + received = vc_container_io_read(read_io, buffer, sizeof(buffer)); + while (ready && total_written < received) + { + total_written += vc_container_io_write(write_io, buffer + total_written, received - total_written); + ready &= (write_io->status == VC_CONTAINER_SUCCESS); + } + ready &= (read_io->status == VC_CONTAINER_SUCCESS); + + if (nb_char_available()) + { + char c = nb_get_char(); + + switch (c) + { + case 'q': + case 'Q': + case 0x04: /* CTRL+D */ + case 0x1A: /* CTRL+Z */ + case 0x1B: /* Escape */ + ready = false; + break; + default: + ;/* Do nothing */ + } + } + } + + if (read_io->status != VC_CONTAINER_SUCCESS && read_io->status != VC_CONTAINER_ERROR_EOS) + { + LOG_INFO(NULL, "Read failed: %d\n", read_io->status); + } + + if (write_io->status != VC_CONTAINER_SUCCESS) + { + LOG_INFO(NULL, "Write failed: %d\n", write_io->status); + } + + vc_container_io_close(read_io); + vc_container_io_close(write_io); + + nb_set_nonblocking_input(0); + + return 0; +} diff --git a/containers/wav/CMakeLists.txt b/containers/wav/CMakeLists.txt new file mode 100755 index 0000000..aed61c6 --- /dev/null +++ b/containers/wav/CMakeLists.txt @@ -0,0 +1,13 @@ +# Container module needs to go in as a plugins so different prefix +# and install path +set(CMAKE_SHARED_LIBRARY_PREFIX "") + +# Make sure the compiler can find the necessary include files +include_directories (../..) + +add_library(reader_wav ${LIBRARY_TYPE} wav_reader.c) + +target_link_libraries(reader_wav containers) + +install(TARGETS reader_wav DESTINATION ${VMCS_PLUGIN_DIR}) + diff --git a/containers/wav/wav_reader.c b/containers/wav/wav_reader.c new file mode 100755 index 0000000..0b279ba --- /dev/null +++ b/containers/wav/wav_reader.c @@ -0,0 +1,366 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +#define CONTAINER_IS_LITTLE_ENDIAN +//#define ENABLE_CONTAINERS_LOG_FORMAT +//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE +#define CONTAINER_HELPER_LOG_INDENT(a) 0 +#include "containers/core/containers_private.h" +#include "containers/core/containers_io_helpers.h" +#include "containers/core/containers_utils.h" +#include "containers/core/containers_logging.h" +#include "containers/core/containers_waveformat.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define WAV_EXTRADATA_MAX 16 +#define BLOCK_SIZE (16*1024) + +/****************************************************************************** +GUID list for the different codecs +******************************************************************************/ +static const GUID_T atracx_guid = {0xbfaa23e9, 0x58cb, 0x7144, {0xa1, 0x19, 0xff, 0xfa, 0x01, 0xe4, 0xce, 0x62}}; +static const GUID_T pcm_guid = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + +/****************************************************************************** +Type definitions +******************************************************************************/ +typedef struct VC_CONTAINER_MODULE_T +{ + uint64_t data_offset; /**< Offset to the start of the data packets */ + int64_t data_size; /**< Size of the data contained in the data element */ + uint32_t block_size; /**< Size of a block of audio data */ + int64_t position; + uint64_t frame_data_left; + + VC_CONTAINER_TRACK_T *track; + uint8_t extradata[WAV_EXTRADATA_MAX]; + +} VC_CONTAINER_MODULE_T; + +/****************************************************************************** +Function prototypes +******************************************************************************/ +VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T * ); + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/***************************************************************************** +Functions exported as part of the Container Module API + *****************************************************************************/ + +static VC_CONTAINER_STATUS_T wav_reader_read( VC_CONTAINER_T *p_ctx, + VC_CONTAINER_PACKET_T *p_packet, uint32_t flags ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + uint32_t packet_flags = 0, size, data_size; + int64_t pts; + + pts = module->position * 8000000 / p_ctx->tracks[0]->format->bitrate; + data_size = module->frame_data_left; + if(!data_size) + { + data_size = module->block_size; + packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START; + } + module->frame_data_left = 0; + + if(module->position + data_size > module->data_size) + data_size = module->data_size - module->position; + if(data_size == 0) return VC_CONTAINER_ERROR_EOS; + + if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */ + { + size = SKIP_BYTES(p_ctx, data_size); + module->frame_data_left = data_size - size; + module->position += size; + return STREAM_STATUS(p_ctx); + } + + p_packet->flags = packet_flags; + p_packet->dts = p_packet->pts = pts; + p_packet->track = 0; + + if(flags & VC_CONTAINER_READ_FLAG_SKIP) + { + size = SKIP_BYTES(p_ctx, data_size); + module->frame_data_left = data_size - size; + if(!module->frame_data_left) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + module->position += size; + p_packet->size += size; + return STREAM_STATUS(p_ctx); + } + + if(flags & VC_CONTAINER_READ_FLAG_INFO) + return VC_CONTAINER_SUCCESS; + + size = MIN(data_size, p_packet->buffer_size - p_packet->size); + size = READ_BYTES(p_ctx, p_packet->data, size); + module->frame_data_left = data_size - size; + if(!module->frame_data_left) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END; + module->position += size; + p_packet->size += size; + + return STREAM_STATUS(p_ctx); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T wav_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset, + VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + int64_t position; + VC_CONTAINER_PARAM_UNUSED(mode); + VC_CONTAINER_PARAM_UNUSED(flags); + + position = *p_offset * p_ctx->tracks[0]->format->bitrate / 8000000; + if(p_ctx->tracks[0]->format->type->audio.block_align) + position = position / p_ctx->tracks[0]->format->type->audio.block_align * + p_ctx->tracks[0]->format->type->audio.block_align; + if(position > module->data_size) position = module->data_size; + + module->position = position; + module->frame_data_left = 0; + + if(position >= module->data_size) return VC_CONTAINER_ERROR_EOS; + return SEEK(p_ctx, module->data_offset + position); +} + +/*****************************************************************************/ +static VC_CONTAINER_STATUS_T wav_reader_close( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; + unsigned int i; + + for(i = 0; i < p_ctx->tracks_num; i++) + vc_container_free_track(p_ctx, p_ctx->tracks[i]); + free(module); + return VC_CONTAINER_SUCCESS; +} + +/*****************************************************************************/ +VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T *p_ctx ) +{ + VC_CONTAINER_MODULE_T *module = 0; + VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; + VC_CONTAINER_FOURCC_T codec; + int64_t chunk_size, chunk_pos; + uint32_t format, channels, samplerate, bitrate, block_align, bps, cbsize = 0; + uint8_t buffer[12]; + + /* Check the RIFF chunk descriptor */ + if( PEEK_BYTES(p_ctx, buffer, 12) != 12 ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if( VC_FOURCC(buffer[0], buffer[1], buffer[2], buffer[3]) != + VC_FOURCC('R','I','F','F') ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + if( VC_FOURCC(buffer[8], buffer[9], buffer[10], buffer[11]) != + VC_FOURCC('W','A','V','E') ) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; + + /* + * We are dealing with a WAV file + */ + SKIP_FOURCC(p_ctx, "Chunk ID"); + SKIP_U32(p_ctx, "Chunk size"); + SKIP_FOURCC(p_ctx, "WAVE ID"); + + /* We're looking for the 'fmt' sub-chunk */ + do { + chunk_pos = STREAM_POSITION(p_ctx) + 8; + if( READ_FOURCC(p_ctx, "Chunk ID") == VC_FOURCC('f','m','t',' ') ) break; + + /* Not interested in this chunk. Skip it. */ + chunk_size = READ_U32(p_ctx, "Chunk size"); + SKIP_BYTES(p_ctx, chunk_size); + } while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* 'fmt' not found */ + + /* Parse the 'fmt' sub-chunk */ + chunk_size = READ_U32(p_ctx, "Chunk size"); + format = READ_U16(p_ctx, "wFormatTag"); + channels = READ_U16(p_ctx, "nChannels"); + samplerate = READ_U32(p_ctx, "nSamplesPerSec"); + bitrate = READ_U32(p_ctx, "nAvgBytesPerSec") * 8; + block_align = READ_U16(p_ctx, "nBlockAlign"); + bps = READ_U16(p_ctx, "wBitsPerSample"); + + if(STREAM_POSITION(p_ctx) - chunk_pos <= chunk_size - 2) + cbsize = READ_U16(p_ctx, "cbSize"); + + if(format == WAVE_FORMAT_EXTENSIBLE && + chunk_size >= 18 + 22 && cbsize >= 22) + { + GUID_T guid; + codec = VC_CONTAINER_CODEC_UNKNOWN; + + SKIP_U16(p_ctx, "wValidBitsPerSample"); + SKIP_U32(p_ctx, "dwChannelMask"); + READ_GUID(p_ctx, &guid, "SubFormat"); + + if(!memcmp(&guid, &pcm_guid, sizeof(guid))) + codec = VC_CONTAINER_CODEC_PCM_SIGNED_LE; + else if(!memcmp(&guid, &atracx_guid, sizeof(guid))) + codec = VC_CONTAINER_CODEC_ATRAC3; + + cbsize -= 22; + + /* TODO: deal with channel mapping */ + } + else + { + codec = waveformat_to_codec(format); + } + + /* Bail out if we don't recognise the codec */ + if(codec == VC_CONTAINER_CODEC_UNKNOWN) + return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED; + + /* Do some sanity checking on the info we got */ + if(!channels || !samplerate || !block_align || !bitrate) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + if(codec == VC_CONTAINER_CODEC_ATRAC3 && channels > 2) + return VC_CONTAINER_ERROR_FORMAT_INVALID; + + /* Allocate our context */ + module = malloc(sizeof(*module)); + if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } + memset(module, 0, sizeof(*module)); + p_ctx->priv->module = module; + p_ctx->tracks_num = 1; + p_ctx->tracks = &module->track; + p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0); + if(!p_ctx->tracks[0]) return VC_CONTAINER_ERROR_OUT_OF_MEMORY; + + p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; + p_ctx->tracks[0]->format->codec = codec; + p_ctx->tracks[0]->format->type->audio.channels = channels; + p_ctx->tracks[0]->format->type->audio.sample_rate = samplerate; + p_ctx->tracks[0]->format->type->audio.block_align = block_align; + p_ctx->tracks[0]->format->type->audio.bits_per_sample = bps; + p_ctx->tracks[0]->format->bitrate = bitrate; + p_ctx->tracks[0]->is_enabled = true; + p_ctx->tracks[0]->format->extradata_size = 0; + p_ctx->tracks[0]->format->extradata = module->extradata; + module->block_size = block_align; + + /* Prepare the codec extradata */ + if(codec == VC_CONTAINER_CODEC_ATRAC3) + { + uint16_t h, mode; + + SKIP_U16(p_ctx, "len"); + SKIP_U16(p_ctx, "layer"); + SKIP_U32(p_ctx, "bytes_per_frame"); + mode = READ_U16(p_ctx, "mode"); + SKIP_U16(p_ctx, "mode_ext"); + SKIP_U16(p_ctx, "num_subframes"); + SKIP_U16(p_ctx, "flags"); + + h = (1 << 15); + if(channels == 2) + { + h |= (1 << 13); + if(mode == 1) h |= (1 << 14); + } + h |= block_align & 0x7ff; + + p_ctx->tracks[0]->format->extradata[0] = h >> 8; + p_ctx->tracks[0]->format->extradata[1] = h & 255; + p_ctx->tracks[0]->format->extradata_size = 2; + } + else if(codec == VC_CONTAINER_CODEC_ATRACX && cbsize >= 6) + { + SKIP_BYTES(p_ctx, 2); + p_ctx->tracks[0]->format->extradata_size = + READ_BYTES(p_ctx, p_ctx->tracks[0]->format->extradata, 6); + } + else if(codec == VC_CONTAINER_CODEC_PCM_SIGNED_LE) + { + /* Audioplus can no longer be given anything other than a multiple-of-16 number of samples */ + block_align *= 16; + module->block_size = (BLOCK_SIZE / block_align) * block_align; + } + + /* Skip the rest of the 'fmt' sub-chunk */ + SKIP_BYTES(p_ctx, chunk_pos + chunk_size - STREAM_POSITION(p_ctx)); + + /* We also need the 'data' sub-chunk */ + do { + chunk_pos = STREAM_POSITION(p_ctx) + 8; + if( READ_FOURCC(p_ctx, "Chunk ID") == VC_FOURCC('d','a','t','a') ) break; + + /* Not interested in this chunk. Skip it. */ + chunk_size = READ_U32(p_ctx, "Chunk size"); + SKIP_BYTES(p_ctx, chunk_size); + } while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS); + + if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) + { + status = VC_CONTAINER_ERROR_FORMAT_INVALID; /* 'data' not found */; + goto error; + } + + module->data_offset = chunk_pos; + module->data_size = READ_U32(p_ctx, "Chunk size"); + p_ctx->duration = module->data_size * 8000000 / bitrate; + if(STREAM_SEEKABLE(p_ctx)) + p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK; + + /* + * We now have all the information we really need to start playing the stream + */ + + p_ctx->priv->pf_close = wav_reader_close; + p_ctx->priv->pf_read = wav_reader_read; + p_ctx->priv->pf_seek = wav_reader_seek; + + /* Seek back to the start of the data */ + status = SEEK(p_ctx, module->data_offset); + if(status != VC_CONTAINER_SUCCESS) goto error; + return VC_CONTAINER_SUCCESS; + + error: + LOG_DEBUG(p_ctx, "wav: error opening stream (%i)", status); + if(module) wav_reader_close(p_ctx); + return status; +} + +/******************************************************************************** + Entrypoint function + ********************************************************************************/ + +#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) +# pragma weak reader_open wav_reader_open +#endif diff --git a/helpers/dtoverlay/CMakeLists.txt b/helpers/dtoverlay/CMakeLists.txt new file mode 100755 index 0000000..b3bd30f --- /dev/null +++ b/helpers/dtoverlay/CMakeLists.txt @@ -0,0 +1,25 @@ +# ============================================================================= +# Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +# All rights reserved. +# +# FILE DESCRIPTION +# CMake build file for dtoverlay library. +# ============================================================================= + +cmake_minimum_required (VERSION 2.8) + +include_directories (${VIDEOCORE_ROOT} + ${VIDEOCORE_ROOT}/helpers + ${VIDEOCORE_ROOT}/opensrc/helpers/libfdt + ${VIDEOCORE_HEADERS_BUILD_DIR}) + +if (CMAKE_COMPILER_IS_GNUCC) + add_definitions (-ffunction-sections) +endif () + +add_library (dtovl ${SHARED} + dtoverlay.c) + +target_link_libraries(dtovl fdt) + +install (TARGETS dtovl DESTINATION lib) diff --git a/helpers/dtoverlay/dtoverlay.c b/helpers/dtoverlay/dtoverlay.c new file mode 100755 index 0000000..29e1195 --- /dev/null +++ b/helpers/dtoverlay/dtoverlay.c @@ -0,0 +1,2006 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "dtoverlay.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +typedef enum +{ + FIXUP_ABSOLUTE, + FIXUP_RELATIVE +} fixup_type_t; + +#define DTOVERRIDE_END 0 +#define DTOVERRIDE_INTEGER 1 +#define DTOVERRIDE_BOOLEAN 2 +#define DTOVERRIDE_STRING 3 +#define DTOVERRIDE_OVERLAY 4 + +static int dtoverlay_extract_override(const char *override_name, + int *phandle_ptr, + const char **data_ptr, int *len_ptr, + const char **namep, int *namelenp, + int *offp, int *sizep); + +static int dtoverlay_set_node_name(DTBLOB_T *dtb, int node_off, + const char *name); + +static void dtoverlay_stdio_logging(dtoverlay_logging_type_t type, + const char *fmt, va_list args); + +#define phandle_debug if (0) dtoverlay_debug + +static DTOVERLAY_LOGGING_FUNC *dtoverlay_logging_func = dtoverlay_stdio_logging; +static int dtoverlay_debug_enabled = 0; + +static uint32_t dtoverlay_read_u32(const void *src, int off) +{ + const unsigned char *p = src; + return (p[off + 0] << 24) | (p[off + 1] << 16) | + (p[off + 2] << 8) | (p[off + 3] << 0); +} + +static void dtoverlay_write_u8(void *dst, int off, uint32_t val) +{ + unsigned char *p = dst; + p[off] = (val >> 0) & 0xff; +} + +static void dtoverlay_write_u16(void *dst, int off, uint32_t val) +{ + unsigned char *p = dst; + p[off + 0] = (val >> 8) & 0xff; + p[off + 1] = (val >> 0) & 0xff; +} + +static void dtoverlay_write_u32(void *dst, int off, uint32_t val) +{ + unsigned char *p = dst; + p[off + 0] = (val >> 24) & 0xff; + p[off + 1] = (val >> 16) & 0xff; + p[off + 2] = (val >> 8) & 0xff; + p[off + 3] = (val >> 0) & 0xff; +} + +static void dtoverlay_write_u64(void *dst, int off, uint64_t val) +{ + unsigned char *p = dst; + p[off + 0] = (val >> 56) & 0xff; + p[off + 1] = (val >> 48) & 0xff; + p[off + 2] = (val >> 40) & 0xff; + p[off + 3] = (val >> 32) & 0xff; + p[off + 4] = (val >> 24) & 0xff; + p[off + 5] = (val >> 16) & 0xff; + p[off + 6] = (val >> 8) & 0xff; + p[off + 7] = (val >> 0) & 0xff; +} + +// Returns the offset of the node indicated by the absolute path, creating +// it and any intermediates as necessary, or a negative error code. +int dtoverlay_create_node(DTBLOB_T *dtb, const char *node_path, int path_len) +{ + const char *path_ptr; + const char *path_end; + int node_off = 0; + + if (!path_len) + path_len = strlen(node_path); + + path_ptr = node_path; + path_end = node_path + path_len; + + if ((path_len > 0) && (path_ptr[path_len - 1] == '/')) + path_end--; + + while (path_ptr < path_end) + { + const char *path_next; + int subnode_off; + + if (*path_ptr != '/') + return -FDT_ERR_BADPATH; + + // find the next path separator (or the end of the string) + path_ptr++; + for (path_next = path_ptr; + (path_next != path_end) && (*path_next != '/'); + path_next++) + continue; + + subnode_off = fdt_subnode_offset_namelen(dtb->fdt, node_off, path_ptr, + path_next - path_ptr); + if (subnode_off >= 0) + node_off = subnode_off; + else + node_off = fdt_add_subnode_namelen(dtb->fdt, node_off, path_ptr, + path_next - path_ptr); + if (node_off < 0) + break; + + path_ptr = path_next; + } + + if ((node_off >= 0) && (path_ptr != path_end)) + return -FDT_ERR_BADPATH; + + return node_off; +} + +// Returns 0 on success, otherwise <0 error code +int dtoverlay_delete_node(DTBLOB_T *dtb, const char *node_path, int path_len) +{ + int node_off = 0; + if (!path_len) + path_len = strlen(node_path); + + dtoverlay_debug("delete_node(%.*s)", path_len, node_path); + node_off = fdt_path_offset_namelen(dtb->fdt, node_path, path_len); + if (node_off < 0) + return node_off; + return fdt_del_node(dtb->fdt, node_off); +} + +// Returns the offset of the node indicated by the absolute path or a negative +// error code. +int dtoverlay_find_node(DTBLOB_T *dtb, const char *node_path, int path_len) +{ + if (!path_len) + path_len = strlen(node_path); + return fdt_path_offset_namelen(dtb->fdt, node_path, path_len); +} + +// Returns 0 on success, otherwise <0 error code +int dtoverlay_set_node_properties(DTBLOB_T *dtb, const char *node_path, + DTOVERLAY_PARAM_T *properties, + unsigned int num_properties) +{ + int err = 0; + int node_off; + + node_off = fdt_path_offset(dtb->fdt, node_path); + if (node_off < 0) + node_off = dtoverlay_create_node(dtb, node_path, 0); + if (node_off >= 0) + { + int i; + for (i = 0; (i < num_properties) && (err == 0); i++) + { + DTOVERLAY_PARAM_T *p; + + p = properties + i; + err = fdt_setprop(dtb->fdt, node_off, p->param, p->b, p->len); + } + } + else + err = node_off; + return err; +} + +struct dynstring +{ + char *buf; + int size; + int len; +}; + +static void dynstring_init(struct dynstring *ds) +{ + ds->size = 0; + ds->len = 0; + ds->buf = NULL; +} + +static int dynstring_init_size(struct dynstring *ds, int initial_size) +{ + if (initial_size < 32) + initial_size = 32; + ds->size = initial_size; + ds->len = 0; + ds->buf = malloc(initial_size); + if (!ds->buf) + { + dtoverlay_error(" out of memory"); + return -FDT_ERR_NOSPACE; + } + return 0; +} + +static int dynstring_set_size(struct dynstring *ds, int size) +{ + if (size > ds->size) + { + size = (size * 5)/4; // Add a 25% headroom + ds->buf = realloc(ds->buf, size); + if (!ds->buf) + { + dtoverlay_error(" out of memory"); + return -FDT_ERR_NOSPACE; + } + ds->size = size; + } + return 0; +} + +static int dynstring_dup(struct dynstring *ds, const char *src, int len) +{ + int err = 0; + + if (!len) + len = strlen(src); + + err = dynstring_set_size(ds, len + 1); + if (!err) + { + memcpy(ds->buf, src, len + 1); + ds->len = len; + } + + return err; +} + +static int dynstring_patch(struct dynstring *ds, int pos, int width, + const char *src, int len) +{ + int newlen = ds->len + (len - width); + int err = dynstring_set_size(ds, newlen + 1); + if (!err) + { + if (width != len) + { + // Move any data following the patch + memmove(ds->buf + pos + len, ds->buf + pos + width, + ds->len + 1 - (pos + width)); + ds->len = newlen; + } + memcpy(ds->buf + pos, src, len); + } + return err; +} + +static int dynstring_grow(struct dynstring *ds) +{ + return dynstring_set_size(ds, (3*ds->size)/2); +} + +static void dynstring_free(struct dynstring *ds) +{ + free(ds->buf); + dynstring_init(ds); +} + +static int dtoverlay_set_node_name(DTBLOB_T *dtb, int node_off, + const char *name) +{ + struct dynstring path_buf; + struct dynstring prop_buf; + char *old_path; + const char *old_name; + const char *fixup_nodes[] = + { + "/__fixups__", + "/__local_fixups__", // For old-style dtbos + "/__symbols__" // Just in case the kernel cares + }; + int old_name_len; + int old_path_len; // All of it + int dir_len; // Excluding the node name, but with the trailling slash + int name_len; + int offset; + int fixup_idx; + int err = 0; + + // Fixups and local-fixups both use node names, so this + // function must be patch them up when a node is renamed + // unless the fixups have already been applied. + // Calculating a node's name is expensive, so only do it if + // necessary. Since renaming a node can move things around, + // don't use node_off afterwards. + err = dynstring_init_size(&path_buf, 100); + if (err) + return err; + + if (!dtb->fixups_applied) + { + while (1) + { + err = fdt_get_path(dtb->fdt, node_off, path_buf.buf, path_buf.size); + if (!err) + break; + if (err != -FDT_ERR_NOSPACE) + return err; + dynstring_grow(&path_buf); + } + } + old_path = path_buf.buf; + + err = fdt_set_name(dtb->fdt, node_off, name); + if (err || dtb->fixups_applied) + goto clean_up; + + // Find the node name in old_path + old_name = strrchr(old_path, '/'); + assert(old_name); + if (!old_name) + return -FDT_ERR_INTERNAL; + old_name++; + old_name_len = strlen(old_name); + dir_len = old_name - old_path; + old_path_len = dir_len + old_name_len; + + // Short-circuit the case where the name isn't changing + if (strcmp(name, old_name) == 0) + goto clean_up; + + name_len = strlen(name); + + // Search the fixups and symbols for the old path (including as + // a parent) and replace with the new name + + dynstring_init(&prop_buf); + for (fixup_idx = 0; fixup_idx < ARRAY_SIZE(fixup_nodes); fixup_idx++) + { + int prop_off; + + offset = fdt_path_offset(dtb->fdt, fixup_nodes[fixup_idx]); + if (offset > 0) + { + + // Iterate through the properties + for (prop_off = fdt_first_property_offset(dtb->fdt, offset); + (prop_off >= 0) && (err == 0); + prop_off = fdt_next_property_offset(dtb->fdt, prop_off)) + { + const char *prop_name; + const char *prop_val; + int prop_len; + int pos; + int changed = 0; + + prop_val = fdt_getprop_by_offset(dtb->fdt, prop_off, + &prop_name, &prop_len); + err = dynstring_dup(&prop_buf, prop_val, prop_len); + if (err) + break; + + // Scan each property for matching paths + pos = 0; + while (pos < prop_len) + { + if ((pos + old_path_len < prop_len) && + (memcmp(prop_buf.buf + pos, old_path, old_path_len) == 0) && + ((prop_buf.buf[pos + old_path_len] == ':') || + (prop_buf.buf[pos + old_path_len] == '/') || + (prop_buf.buf[pos + old_path_len] == '\0'))) + { + // Patch the string, replacing old name with new + err = dynstring_patch(&prop_buf, pos + dir_len, old_name_len, + name, name_len); + if (err) + break; + + prop_len += name_len - old_name_len; + changed = 1; + } + pos += strlen(prop_buf.buf + pos) + 1; + } + + if (!err && changed) + { + // Caution - may change offsets, but only by shuffling everything + // afterwards, i.e. the offset to this node or property does not + // change. + err = fdt_setprop(dtb->fdt, offset, prop_name, prop_buf.buf, + prop_len); + } + } + } + } + + dynstring_free(&prop_buf); + + if (err) + goto clean_up; + + // Then look for a "/__local_fixups__" node, and rename + // that as well. + offset = fdt_path_offset(dtb->fdt, "/__local_fixups__"); + if (offset > 0) + { + const char *p, *end; + + p = old_path; + end = old_path + old_path_len; + while (p < end) + { + const char *q; + + while (*p == '/') { + p++; + if (p == end) + break; + } + q = memchr(p, '/', end - p); + if (! q) + q = end; + + offset = fdt_subnode_offset_namelen(dtb->fdt, offset, p, q-p); + if (offset < 0) + break; + + p = q; + } + + if (offset > 0) + err = fdt_set_name(dtb->fdt, offset, name); + } + + // __overrides__ don't need patching because nodes are identified + // using phandles, which are unaffected by renaming and resizing nodes. + +clean_up: + dynstring_free(&path_buf); + + return err; +} + +// Returns 0 on success, otherwise <0 error code +int dtoverlay_create_prop_fragment(DTBLOB_T *dtb, int idx, int target_phandle, + const char *prop_name, const void *prop_data, + int prop_len) +{ + char fragment_name[20]; + int frag_off, ovl_off; + int ret; + snprintf(fragment_name, sizeof(fragment_name), "fragment@%u", idx); + frag_off = fdt_add_subnode(dtb->fdt, 0, fragment_name); + if (frag_off < 0) + return frag_off; + ret = fdt_setprop_u32(dtb->fdt, frag_off, "target", target_phandle); + if (ret < 0) + return ret; + ovl_off = fdt_add_subnode(dtb->fdt, frag_off, "__overlay__"); + if (ovl_off < 0) + return ovl_off; + return fdt_setprop(dtb->fdt, ovl_off, prop_name, prop_data, prop_len); +} + +// Returns 0 on success, otherwise <0 error code +static int dtoverlay_merge_fragment(DTBLOB_T *base_dtb, int target_off, + const DTBLOB_T *overlay_dtb, + int overlay_off, int depth) +{ + int prop_off, subnode_off; + int err = 0; + + if (dtoverlay_debug_enabled) + { + char base_path[256]; + char overlay_path[256]; + fdt_get_path(base_dtb->fdt, target_off, base_path, sizeof(base_path)); + fdt_get_path(overlay_dtb->fdt, overlay_off, overlay_path, + sizeof(overlay_path)); + + dtoverlay_debug("merge_fragment(%s,%s)", base_path, + overlay_path); + } + + // Merge each property of the node + for (prop_off = fdt_first_property_offset(overlay_dtb->fdt, overlay_off); + (prop_off >= 0) && (err == 0); + prop_off = fdt_next_property_offset(overlay_dtb->fdt, prop_off)) + { + const char *prop_name; + const void *prop_val; + int prop_len; + struct fdt_property *target_prop; + int target_len; + + prop_val = fdt_getprop_by_offset(overlay_dtb->fdt, prop_off, + &prop_name, &prop_len); + + /* Skip these system properties (only phandles in the first level) */ + if ((strcmp(prop_name, "name") == 0) || + ((depth == 0) && ((strcmp(prop_name, "phandle") == 0) || + (strcmp(prop_name, "linux,phandle") == 0)))) + continue; + + dtoverlay_debug(" +prop(%s)", prop_name); + + if ((strcmp(prop_name, "bootargs") == 0) && + ((target_prop = fdt_get_property_w(base_dtb->fdt, target_off, prop_name, &target_len)) != NULL) && + (target_len > 0) && *target_prop->data) + { + target_prop->data[target_len - 1] = ' '; + err = fdt_appendprop(base_dtb->fdt, target_off, prop_name, prop_val, prop_len); + } + else + err = fdt_setprop(base_dtb->fdt, target_off, prop_name, prop_val, prop_len); + } + + // Merge each subnode of the node + for (subnode_off = fdt_first_subnode(overlay_dtb->fdt, overlay_off); + (subnode_off >= 0) && (err == 0); + subnode_off = fdt_next_subnode(overlay_dtb->fdt, subnode_off)) + { + const char *subnode_name; + int name_len; + int subtarget_off; + + subnode_name = fdt_get_name(overlay_dtb->fdt, subnode_off, &name_len); + + subtarget_off = fdt_subnode_offset_namelen(base_dtb->fdt, target_off, + subnode_name, name_len); + if (subtarget_off < 0) + subtarget_off = fdt_add_subnode_namelen(base_dtb->fdt, target_off, + subnode_name, name_len); + + if (subtarget_off >= 0) + { + err = dtoverlay_merge_fragment(base_dtb, subtarget_off, + overlay_dtb, subnode_off, + depth + 1); + } + else + { + err = subtarget_off; + } + } + + dtoverlay_debug("merge_fragment() end"); + + return err; +} + +static int dtoverlay_phandle_relocate(DTBLOB_T *dtb, int node_off, + const char *prop_name, + uint32_t phandle_increment) +{ + int len; + const fdt32_t *prop_val = fdt_getprop(dtb->fdt, node_off, prop_name, &len); + int err = 0; // The absence of the property is not an error + + if (prop_val) + { + uint32_t phandle; + + if (len < 4) + { + dtoverlay_error("%s property too small", prop_name); + return -FDT_ERR_BADSTRUCTURE; + } + + phandle = fdt32_to_cpu(*prop_val) + phandle_increment; + phandle_debug(" phandle_relocate %d->%d", fdt32_to_cpu(*prop_val), phandle); + + err = fdt_setprop_inplace_u32(dtb->fdt, node_off, prop_name, phandle); + } + + return err; +} + +// Returns 0 on success, or an FDT error code +static int dtoverlay_apply_fixups(DTBLOB_T *dtb, const char *fixups_stringlist, + uint32_t phandle, fixup_type_t type) +{ + // The fixups arrive as a sequence of NUL-terminated strings, of the form: + // "path:property:offset" + // Use an empty string as an end marker, since: + // 1) all tags begin 0x00 0x00 0x00, + // 2) all string properties must be followed by a tag, + // 3) an empty string is not a valid fixup, and + // 4) the code is simpler as a result. + + const char *fixup = fixups_stringlist; + + while (fixup[0]) + { + const char *prop_name, *offset_str; + char *offset_end; + const void *prop_ptr; + int prop_len; + int node_off; + unsigned long offset; + uint32_t patch; + + prop_name = strchr(fixup, ':'); + if (!prop_name) + return -FDT_ERR_BADSTRUCTURE; + prop_name++; + + offset_str = strchr(prop_name, ':'); + if (!offset_str) + return -FDT_ERR_BADSTRUCTURE; + offset_str++; + + offset = strtoul(offset_str, &offset_end, 10); + if ((offset_end == offset_str) || (offset_end[0] != 0)) + return -FDT_ERR_BADSTRUCTURE; + + node_off = fdt_path_offset_namelen(dtb->fdt, fixup, prop_name - 1 - fixup); + if (node_off < 0) + return node_off; + + prop_ptr = fdt_getprop_namelen(dtb->fdt, node_off, prop_name, + offset_str - 1 - prop_name, &prop_len); + if (!prop_ptr) + return prop_len; + if (offset > (prop_len - 4)) + return -FDT_ERR_BADSTRUCTURE; + + // Now apply the patch. Yes, prop_ptr is a const void *, but the + // alternative (copying the whole property, patching, then updating as + // a whole) is ridiculous. + if (type == FIXUP_RELATIVE) + { + patch = phandle + dtoverlay_read_u32(prop_ptr, offset); + phandle_debug(" phandle fixup %d+%d->%d", phandle, patch - phandle, patch); + } + else + { + patch = phandle; + phandle_debug(" phandle ref '%s'->%d", prop_name, patch); + } + + dtoverlay_write_u32((void *)prop_ptr, offset, patch); + + fixup = offset_end + 1; + } + + return 0; +} + +// Returns 0 on success, or an FDT error code +static int dtoverlay_apply_fixups_node(DTBLOB_T *dtb, int fix_off, + int target_off, uint32_t phandle_offset) +{ + // The fixups are arranged as a subtree mirroring the structure of the + // overall tree. Walk this tree in order. Each property is an array of cells + // containing offsets to patch within the corresponding node/property of + // the target tree. + int err = 0; + int prop_off; + int subfix_off; + + // Merge each property of the node + for (prop_off = fdt_first_property_offset(dtb->fdt, fix_off); + (prop_off >= 0) && (err == 0); + prop_off = fdt_next_property_offset(dtb->fdt, prop_off)) + { + const char *prop_name; + const void *prop_val; + int prop_len; + void *target_ptr; + int target_len; + int off; + + prop_val = fdt_getprop_by_offset(dtb->fdt, prop_off, + &prop_name, &prop_len); + if (!prop_val) + return -FDT_ERR_INTERNAL; + + target_ptr = fdt_getprop_w(dtb->fdt, target_off, prop_name, &target_len); + if (!target_ptr) + return -FDT_ERR_BADSTRUCTURE; + + for (off = 0; (off + 4) <= prop_len; off += 4) + { + uint32_t patch; + int patch_offset = dtoverlay_read_u32(prop_val, off); + if ((patch_offset + 4) > target_len) + return -FDT_ERR_BADSTRUCTURE; + + patch = phandle_offset + dtoverlay_read_u32(target_ptr, patch_offset); + phandle_debug(" phandle fixup %d+%d->%d", phandle_offset, patch - phandle_offset, patch); + + dtoverlay_write_u32(target_ptr, patch_offset, patch); + } + } + + // Merge each subnode of the node + for (subfix_off = fdt_first_subnode(dtb->fdt, fix_off); + (subfix_off >= 0) && (err == 0); + subfix_off = fdt_next_subnode(dtb->fdt, subfix_off)) + { + const char *subnode_name; + int name_len; + int subtarget_off; + + subnode_name = fdt_get_name(dtb->fdt, subfix_off, &name_len); + + subtarget_off = fdt_subnode_offset_namelen(dtb->fdt, target_off, + subnode_name, name_len); + + if (subtarget_off >= 0) + { + err = dtoverlay_apply_fixups_node(dtb, subfix_off, subtarget_off, + phandle_offset); + } + else + { + err = subtarget_off; + } + } + + return err; +} + +// Returns 0 on success, or a negative FDT error. +static int dtoverlay_resolve_phandles(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) +{ + int local_fixups_off; + int node_off; + int err = 0; + + // First find and update the phandles in the overlay + + for (node_off = 0; + node_off >= 0; + node_off = fdt_next_node(overlay_dtb->fdt, node_off, NULL)) + { + dtoverlay_phandle_relocate(overlay_dtb, node_off, "phandle", + base_dtb->max_phandle); + dtoverlay_phandle_relocate(overlay_dtb, node_off, "linux,phandle", + base_dtb->max_phandle); + } + + local_fixups_off = fdt_path_offset(overlay_dtb->fdt, "/__local_fixups__"); + if (local_fixups_off >= 0) + { + const char *fixups_stringlist; + + // Update the references to local phandles using the local fixups. + // The property name is "fixup". + // The value is a NUL-separated stringlist of descriptors of the form: + // path:property:offset + fixups_stringlist = + fdt_getprop(overlay_dtb->fdt, local_fixups_off, "fixup", &err); + if (fixups_stringlist) + { + // Relocate the overlay phandle references + err = dtoverlay_apply_fixups(overlay_dtb, fixups_stringlist, + base_dtb->max_phandle, FIXUP_RELATIVE); + } + else + { + err = dtoverlay_apply_fixups_node(overlay_dtb, local_fixups_off, + 0, base_dtb->max_phandle); + } + if (err < 0) + { + dtoverlay_error("error applying local fixups"); + return err; + } + } + + overlay_dtb->max_phandle += base_dtb->max_phandle; + phandle_debug(" +overlay max phandle +%d -> %d", base_dtb->max_phandle, overlay_dtb->max_phandle); + + return err; +} + +// Returns 0 on success, or an FDT error code +static int dtoverlay_resolve_fixups(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) +{ + int fixups_off; + int err = 0; + + fixups_off = fdt_path_offset(overlay_dtb->fdt, "/__fixups__"); + + if (fixups_off >= 0) + { + int fixup_off, symbols_off = -1; + + fixup_off = fdt_first_property_offset(overlay_dtb->fdt, fixups_off); + + if (fixup_off >= 0) + { + // Find the symbols, which will be needed to resolve the fixups + symbols_off = fdt_path_offset(base_dtb->fdt, "/__symbols__"); + + if (symbols_off < 0) + { + dtoverlay_error("No symbols found"); + return -FDT_ERR_NOTFOUND; + } + } + + for (; + fixup_off >= 0; + fixup_off = fdt_next_property_offset(overlay_dtb->fdt, fixup_off)) + { + const char *fixups_stringlist, *symbol_name, *target_path; + const char *ref_type; + int target_off; + uint32_t target_phandle; + + // The property name identifies a symbol (or alias) in the base. + // The value is a comma-separated list of descriptors of the form: + // path:property:offset + fixups_stringlist = fdt_getprop_by_offset(overlay_dtb->fdt, fixup_off, + &symbol_name, &err); + if (!fixups_stringlist) + { + dtoverlay_error("__fixups__ are borked"); + break; + } + + // 1) Find the target node. + if (symbol_name[0] == '/') + { + /* This is a new-style path reference */ + target_path = symbol_name; + ref_type = "path"; + } + else + { + target_path = fdt_getprop(base_dtb->fdt, symbols_off, symbol_name, + &err); + if (!target_path) + { + dtoverlay_error("can't find symbol '%s'", symbol_name); + break; + } + + ref_type = "symbol"; + } + + target_off = fdt_path_offset(base_dtb->fdt, target_path); + if (target_off < 0) + { + dtoverlay_error("%s '%s' is invalid", ref_type, symbol_name); + err = target_off; + break; + } + + // 2) Ensure that the target node has a phandle. + target_phandle = fdt_get_phandle(base_dtb->fdt, target_off); + if (!target_phandle) + { + // It doesn't, so give it one + fdt32_t temp; + target_phandle = ++base_dtb->max_phandle; + temp = cpu_to_fdt32(target_phandle); + + err = fdt_setprop(base_dtb->fdt, target_off, "phandle", + &temp, 4); + + if (err != 0) + { + dtoverlay_error("failed to add a phandle"); + break; + } + phandle_debug(" phandle '%s'->%d", target_path, target_phandle); + + // The symbols may have moved, so recalculate + symbols_off = fdt_path_offset(base_dtb->fdt, "/__symbols__"); + } + + // Now apply the valid target_phandle to the items in the fixup string + + err = dtoverlay_apply_fixups(overlay_dtb, fixups_stringlist, + target_phandle, FIXUP_ABSOLUTE); + if (err) + break; + } + } + + return err; +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) +{ + // Merge each fragment node + int frag_off; + + for (frag_off = fdt_first_subnode(overlay_dtb->fdt, 0); + frag_off >= 0; + frag_off = fdt_next_subnode(overlay_dtb->fdt, frag_off)) + { + const char *node_name, *target_path; + const char *frag_name; + int target_off, overlay_off; + int len, err; + + node_name = fdt_get_name(overlay_dtb->fdt, frag_off, NULL); + + if (strncmp(node_name, "fragment@", 9) != 0) + continue; + frag_name = node_name + 9; + + dtoverlay_debug("Found fragment %s (offset %d)", frag_name, frag_off); + + // Find the target and overlay nodes + overlay_off = fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__overlay__"); + if (overlay_off < 0) + { + if (fdt_subnode_offset(overlay_dtb->fdt, frag_off, "__dormant__")) + dtoverlay_debug("fragment %s disabled", frag_name); + else + dtoverlay_error("no overlay in fragment %s", frag_name); + continue; + } + + target_path = fdt_getprop(overlay_dtb->fdt, frag_off, "target-path", &len); + if (target_path) + { + if (len && (target_path[len - 1] == '\0')) + len--; + target_off = fdt_path_offset_namelen(base_dtb->fdt, target_path, len); + if (target_off < 0) + { + dtoverlay_error("invalid target-path '%.*s'", len, target_path); + return NON_FATAL(target_off); + } + } + else + { + const void *target_prop; + target_prop = fdt_getprop(overlay_dtb->fdt, frag_off, "target", &len); + if (!target_prop) + { + dtoverlay_error("no target or target-path"); + return NON_FATAL(len); + } + + if (len != 4) + return NON_FATAL(FDT_ERR_BADSTRUCTURE); + + target_off = + fdt_node_offset_by_phandle(base_dtb->fdt, + fdt32_to_cpu(*(fdt32_t *)target_prop)); + if (target_off < 0) + { + dtoverlay_error("invalid target"); + return NON_FATAL(target_off); + } + } + + // Now do the merge + err = dtoverlay_merge_fragment(base_dtb, target_off, overlay_dtb, + overlay_off, 0); + if (err != 0) + { + dtoverlay_error("merge failed"); + return err; + } + } + + base_dtb->max_phandle = overlay_dtb->max_phandle; + + return 0; +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +int dtoverlay_fixup_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb) +{ + int err; + + // To do: Check the "compatible" string? + + err = dtoverlay_resolve_fixups(base_dtb, overlay_dtb); + + if (err >= 0) + err = dtoverlay_resolve_phandles(base_dtb, overlay_dtb); + + overlay_dtb->fixups_applied = 1; + + return NON_FATAL(err); +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +int dtoverlay_merge_params(DTBLOB_T *dtb, const DTOVERLAY_PARAM_T *params, + unsigned int num_params) +{ + int err = 0; + unsigned int i; + + for (i=0; (iparam; + slash = strrchr(node_name, '/'); + + if (!slash) + { + err = NON_FATAL(FDT_ERR_BADPATH); + break; + } + + // Ensure that root properties ("/xxx") work + if (slash == node_name) + path_len = 1; + else + path_len = slash - node_name; + + // find node, create if it does not exist yet + node_off = dtoverlay_create_node(dtb, node_name, path_len); + if (node_off >= 0) + { + const char *prop_name = slash + 1; + int prop_len; + struct fdt_property *prop; + + if ((strcmp(prop_name, "bootargs") == 0) && + ((prop = fdt_get_property_w(dtb->fdt, node_off, prop_name, &prop_len)) != NULL) && + (prop_len > 0) && *prop->data) + { + prop->data[prop_len - 1] = ' '; + err = fdt_appendprop(dtb->fdt, node_off, prop_name, p->b, p->len); + } + else + err = fdt_setprop(dtb->fdt, node_off, prop_name, p->b, p->len); + } + else + err = node_off; + } + + return err; +} + +/* Returns a pointer to the override data and (through data_len) its length. + On error, sets *data_len to be the error code. */ +const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name, + int *data_len) +{ + int overrides_off; + const char *data; + int len; + + // Find the table of overrides + overrides_off = fdt_path_offset(dtb->fdt, "/__overrides__"); + + if (overrides_off < 0) + { + dtoverlay_debug("/__overrides__ node not found"); + *data_len = overrides_off; + return NULL; + } + + // Locate the property + data = fdt_getprop(dtb->fdt, overrides_off, override_name, &len); + *data_len = len; + if (data) + dtoverlay_debug("Found override %s", override_name); + else + dtoverlay_debug("/__overrides__ has no %s property", override_name); + + return data; +} + +int dtoverlay_override_one_target(int override_type, + DTBLOB_T *dtb, int node_off, + const char *prop_name, int target_phandle, + int target_off, int target_size, + void *callback_value) +{ + const char *override_value = callback_value; + int err = 0; + + if (override_type == DTOVERRIDE_STRING) + { + char *prop_val; + int prop_len; + + /* Replace the whole property with the string */ + if (strcmp(prop_name, "status") == 0) + { + /* Convert booleans to okay/disabled */ + if ((strcmp(override_value, "y") == 0) || + (strcmp(override_value, "yes") == 0) || + (strcmp(override_value, "on") == 0) || + (strcmp(override_value, "true") == 0) || + (strcmp(override_value, "enable") == 0) || + (strcmp(override_value, "1") == 0)) + override_value = "okay"; + else if ((strcmp(override_value, "n") == 0) || + (strcmp(override_value, "no") == 0) || + (strcmp(override_value, "off") == 0) || + (strcmp(override_value, "false") == 0) || + (strcmp(override_value, "0") == 0)) + override_value = "disabled"; + } + + if ((strcmp(prop_name, "bootargs") == 0) && + ((prop_val = fdt_getprop_w(dtb->fdt, node_off, prop_name, + &prop_len)) != NULL) && + (prop_len > 0) && prop_val[0]) + { + prop_val[prop_len - 1] = ' '; + err = fdt_appendprop_string(dtb->fdt, node_off, prop_name, override_value); + } + else + err = fdt_setprop_string(dtb->fdt, node_off, prop_name, override_value); + } + else if (override_type != DTOVERRIDE_END) + { + const char *p; + char *end; + char *prop_val; + char *prop_buf = NULL; + int prop_len; + int new_prop_len; + uint64_t override_int; + uint32_t frag_num; + + /* Parse as an integer */ + override_int = strtoull(override_value, &end, 0); + if (end[0] != '\0') + { + if ((strcmp(override_value, "y") == 0) || + (strcmp(override_value, "yes") == 0) || + (strcmp(override_value, "on") == 0) || + (strcmp(override_value, "true") == 0) || + (strcmp(override_value, "down") == 0)) + override_int = 1; + else if ((strcmp(override_value, "n") == 0) || + (strcmp(override_value, "no") == 0) || + (strcmp(override_value, "off") == 0) || + (strcmp(override_value, "false") == 0)) + override_int = 0; + else if (strcmp(override_value, "up") == 0) + override_int = 2; + else + { + dtoverlay_error("invalid override value '%s' - ignored", + override_value); + return NON_FATAL(FDT_ERR_INTERNAL); + } + } + + switch (override_type) + { + case DTOVERRIDE_INTEGER: + /* Patch a word within the property */ + prop_val = (void *)fdt_getprop(dtb->fdt, node_off, prop_name, + &prop_len); + new_prop_len = target_off + target_size; + if (prop_len < new_prop_len) + { + /* This property either doesn't exist or isn't long enough. + Create a buffer containing any existing property data + with zero padding, which will later be patched and written + back. */ + prop_buf = calloc(1, new_prop_len); + if (!prop_buf) + { + dtoverlay_error(" out of memory"); + return NON_FATAL(FDT_ERR_NOSPACE); + } + if (prop_len > 0) + memcpy(prop_buf, prop_val, prop_len); + prop_val = prop_buf; + } + + switch (target_size) + { + case 1: + dtoverlay_write_u8(prop_val, target_off, (uint32_t)override_int); + break; + case 2: + dtoverlay_write_u16(prop_val, target_off, (uint32_t)override_int); + break; + case 4: + dtoverlay_write_u32(prop_val, target_off, (uint32_t)override_int); + break; + case 8: + dtoverlay_write_u64(prop_val, target_off, override_int); + break; + default: + break; + } + + if (prop_buf) + { + /* Add/extend the property by setting it */ + err = fdt_setprop(dtb->fdt, node_off, prop_name, prop_buf, new_prop_len); + free(prop_buf); + } + + if (strcmp(prop_name, "reg") == 0 && target_off == 0) + { + const char *old_name = fdt_get_name(dtb->fdt, node_off, NULL); + const char *atpos = strchr(old_name, '@'); + if (atpos) + { + int name_len = (atpos - old_name); + char *new_name = malloc(name_len + 1 + 16 + 1); + if (!new_name) + return -FDT_ERR_NOSPACE; + sprintf(new_name, "%.*s@%x", name_len, old_name, (uint32_t)override_int); + err = dtoverlay_set_node_name(dtb, node_off, new_name); + free(new_name); + } + } + break; + + case DTOVERRIDE_BOOLEAN: + /* This is a boolean property (present->true, absent->false) */ + if (override_int) + err = fdt_setprop(dtb->fdt, node_off, prop_name, NULL, 0); + else + { + err = fdt_delprop(dtb->fdt, node_off, prop_name); + if (err == -FDT_ERR_NOTFOUND) + err = 0; + } + break; + + case DTOVERRIDE_OVERLAY: + /* Apply the overlay-wide override. The supported options are ( is a fragment number): + + Enable a fragment + - Disable a fragment + = Enable/disable the fragment according to the override value + ! Disable/enable the fragment according to the inverse of the override value */ + p = prop_name; + while (*p && !err) + { + char type = *p; + int frag_off; + switch (type) + { + case '+': + case '-': + case '=': + case '!': + frag_num = strtoul(p + 1, &end, 0); + if (end != p) + { + /* Change fragment@/__overlay__<->__dormant__ as necessary */ + const char *states[2] = { "__dormant__", "__overlay__" }; + char node_name[24]; + int active = (type == '+') || + ((type == '=') && (override_int != 0)) || + ((type == '!') && (override_int == 0)); + snprintf(node_name, sizeof(node_name), "/fragment@%u", frag_num); + frag_off = fdt_path_offset(dtb->fdt, node_name); + if (frag_off >= 0) + { + frag_off = fdt_subnode_offset(dtb->fdt, frag_off, states[!active]); + if (frag_off >= 0) + (void)dtoverlay_set_node_name(dtb, frag_off, states[active]); + } + else + { + dtoverlay_error(" fragment %u not found", frag_num); + err = NON_FATAL(frag_off); + } + p = end; + } + else + { + dtoverlay_error(" invalid overlay override '%s'", prop_name); + err = NON_FATAL(FDT_ERR_BADVALUE); + } + break; + } + } + break; + } + } + + return err; +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +// After calling this, assume all node offsets are no longer valid +int dtoverlay_foreach_override_target(DTBLOB_T *dtb, const char *override_name, + const char *override_data, int data_len, + override_callback_t callback, + void *callback_value) +{ + int err = 0; + int target_phandle = 0; + char *data; + + /* Short-circuit the degenerate case of an empty parameter, avoiding an + apparent memory allocation failure. */ + if (!data_len) + return 0; + + /* Copy the override data in case it moves */ + data = malloc(data_len); + if (!data) + { + dtoverlay_error(" out of memory"); + return NON_FATAL(FDT_ERR_NOSPACE); + } + + memcpy(data, override_data, data_len); + override_data = data; + + while (err == 0) + { + const char *target_prop = NULL; + char *prop_name = NULL; + int name_len = 0; + int target_off = 0; + int target_size = 0; + int override_type; + int node_off = 0; + + override_type = dtoverlay_extract_override(override_name, + &target_phandle, + &override_data, &data_len, + &target_prop, &name_len, + &target_off, &target_size); + + /* Pass DTOVERRIDE_END to the callback, in case it is interested */ + + if (target_phandle != 0) + { + node_off = fdt_node_offset_by_phandle(dtb->fdt, target_phandle); + if (node_off < 0) + { + dtoverlay_error(" phandle %d not found", target_phandle); + err = NON_FATAL(node_off); + break; + } + } + + if (target_prop) + { + /* Sadly there are no '_namelen' setprop variants, so a copy is required */ + prop_name = malloc(name_len + 1); + if (!prop_name) + { + dtoverlay_error(" out of memory"); + err = NON_FATAL(FDT_ERR_NOSPACE); + break; + } + memcpy(prop_name, target_prop, name_len); + prop_name[name_len] = '\0'; + } + + err = callback(override_type, dtb, node_off, prop_name, + target_phandle, target_off, target_size, + callback_value); + + if (prop_name) + free(prop_name); + + if (override_type == DTOVERRIDE_END) + break; + } + + free(data); + + return err; +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +int dtoverlay_apply_override(DTBLOB_T *dtb, const char *override_name, + const char *override_data, int data_len, + const char *override_value) +{ + return dtoverlay_foreach_override_target(dtb, override_name, + override_data, data_len, + dtoverlay_override_one_target, + (void *)override_value); +} + +/* Returns an override type (DTOVERRIDE_INTEGER, DTOVERRIDE_BOOLEAN, DTOVERRIDE_STRING, DTOVERRIDE_OVERLAY), + DTOVERRIDE_END (0) at the end, or an error code (< 0) */ +static int dtoverlay_extract_override(const char *override_name, + int *phandle_ptr, + const char **data_ptr, int *len_ptr, + const char **namep, int *namelenp, + int *offp, int *sizep) +{ + const char *data; + const char *prop_name, *override_end; + int len, override_len, name_len, phandle; + const char *offset_seps = ".;:#?"; + int type; + + data = *data_ptr; + len = *len_ptr; + if (len <= 0) + { + if (len < 0) + return len; + *phandle_ptr = 0; + *namep = NULL; + return DTOVERRIDE_END; + } + + // Check for space for a phandle, a terminating NUL and at least one char + if (len < (sizeof(fdt32_t) + 1 + 1)) + { + dtoverlay_error(" override %s: data is truncated or mangled", + override_name); + return -FDT_ERR_BADSTRUCTURE; + } + + phandle = dtoverlay_read_u32(data, 0); + *phandle_ptr = phandle; + + data += sizeof(fdt32_t); + len -= sizeof(fdt32_t); + + override_end = memchr(data, 0, len); + if (!override_end) + { + dtoverlay_error(" override %s: string is not NUL-terminated", + override_name); + return -FDT_ERR_BADSTRUCTURE; + } + + prop_name = data; + + override_len = override_end - prop_name; + *data_ptr = data + (override_len + 1); + *len_ptr = len - (override_len + 1); + + if (phandle <= 0) + { + if (phandle < 0) + return -FDT_ERR_BADPHANDLE; + /* This is an "overlay" override, signalled using <0> as the phandle. */ + *namep = prop_name; + *namelenp = override_len; + return DTOVERRIDE_OVERLAY; + } + + name_len = strcspn(prop_name, offset_seps); + + *namep = prop_name; + *namelenp = name_len; + if (name_len < override_len) + { + /* There is a separator specified */ + char sep = prop_name[name_len]; + if (sep == '?') + { + /* The target is a boolean parameter (present->true, absent->false) */ + *offp = 0; + *sizep = 0; + type = DTOVERRIDE_BOOLEAN; + dtoverlay_debug(" override %s: boolean target %.*s", + override_name, name_len, prop_name); + } + else + { + /* The target is a cell/integer */ + *offp = atoi(prop_name + name_len + 1); + *sizep = 1 << (strchr(offset_seps, sep) - offset_seps); + type = DTOVERRIDE_INTEGER; + dtoverlay_debug(" override %s: cell target %.*s @ offset %d (size %d)", + override_name, name_len, prop_name, *offp, *sizep); + } + } + else + { + *offp = -1; + *sizep = 0; + type = DTOVERRIDE_STRING; + dtoverlay_debug(" override %s: string target '%.*s'", + override_name, name_len, prop_name); + } + + return type; +} + +int dtoverlay_set_synonym(DTBLOB_T *dtb, const char *dst, const char *src) +{ + /* Add/update all aliases, symbols and overrides named dst + to be equivalent to those named src. + An absent src is ignored. + */ + int err; + + err = dtoverlay_dup_property(dtb, "/aliases", dst, src); + if (err == 0) + err = dtoverlay_dup_property(dtb, "/__symbols__", dst, src); + if (err == 0) + dtoverlay_dup_property(dtb, "/__overrides__", dst, src); + return err; +} + +int dtoverlay_dup_property(DTBLOB_T *dtb, const char *node_name, + const char *dst, const char *src) +{ + /* Find the node and src property */ + const DTBLOB_T *src_prop; + int node_off; + int prop_len = 0; + int err = 0; + + node_off = fdt_path_offset(dtb->fdt, node_name); + if (node_off < 0) + return 0; + + src_prop = fdt_getprop(dtb->fdt, node_off, src, &prop_len); + if (!src_prop) + return 0; + + err = fdt_setprop_inplace(dtb->fdt, node_off, dst, src_prop, prop_len); + if (err != 0) + { + void *prop_data; + /* Copy the src property, just in case things move */ + prop_data = malloc(prop_len); + memcpy(prop_data, src_prop, prop_len); + + err = fdt_setprop(dtb->fdt, node_off, dst, prop_data, prop_len); + + free(prop_data); + } + + if (err == 0) + dtoverlay_debug("%s:%s=%s", node_name, dst, src); + return err; +} + +DTBLOB_T *dtoverlay_create_dtb(int max_size) +{ + DTBLOB_T *dtb = NULL; + void *fdt = NULL; + + fdt = malloc(max_size); + if (!fdt) + { + dtoverlay_error("out of memory"); + goto error_exit; + } + + if (fdt_create_empty_tree(fdt, max_size) != 0) + { + dtoverlay_error("failed to create empty dtb"); + goto error_exit; + } + + dtb = calloc(1, sizeof(DTBLOB_T)); + if (!dtb) + { + dtoverlay_error("out of memory"); + goto error_exit; + } + + dtb->fdt = fdt; + dtb->max_phandle = 0; // Not a valid phandle + + return dtb; + +error_exit: + free(fdt); + if (dtb) + free(dtb->trailer); + free(dtb); + return NULL; + +} + +DTBLOB_T *dtoverlay_load_dtb_from_fp(FILE *fp, int max_size) +{ + DTBLOB_T *dtb = NULL; + void *fdt = NULL; + + if (fp) + { + long len; + long bytes_read; + int dtb_len; + + fseek(fp, 0, SEEK_END); + len = ftell(fp); + fseek(fp, 0, SEEK_SET); + if (max_size > 0) + { + if (max_size < len) + { + dtoverlay_error("file too large (%d bytes) for max_size", len); + goto error_exit; + } + } + else if (max_size < 0) + { + max_size = len - max_size; + } + else + { + max_size = len; + } + + fdt = malloc(max_size); + if (!fdt) + { + dtoverlay_error("out of memory"); + goto error_exit; + } + + bytes_read = fread(fdt, 1, len, fp); + fclose(fp); + + if (bytes_read != len) + { + dtoverlay_error("fread failed"); + goto error_exit; + } + + // Record the total size before any expansion + dtb_len = fdt_totalsize(fdt); + + dtb = dtoverlay_import_fdt(fdt, max_size); + if (!dtb) + goto error_exit; + + dtb->fdt_is_malloced = 1; + + if (len > dtb_len) + { + /* Load the trailer */ + dtb->trailer_len = len - dtb_len; + dtb->trailer = malloc(dtb->trailer_len); + if (!dtb->trailer) + { + dtoverlay_error("out of memory"); + goto error_exit; + } + dtb->trailer_is_malloced = 1; + memcpy(dtb->trailer, (char *)fdt + dtb_len, dtb->trailer_len); + } + } + + return dtb; + +error_exit: + free(fdt); + if (dtb) + free(dtb->trailer); + free(dtb); + return NULL; +} + +DTBLOB_T *dtoverlay_load_dtb(const char *filename, int max_size) +{ + FILE *fp = fopen(filename, "rb"); + if (fp) + return dtoverlay_load_dtb_from_fp(fp, max_size); + dtoverlay_error("Failed to open '%s'", filename); + return NULL; +} + +DTBLOB_T *dtoverlay_import_fdt(void *fdt, int buf_size) +{ + DTBLOB_T *dtb = NULL; + int node_off; + int dtb_len; + int err; + + err = fdt_check_header(fdt); + if (err != 0) + { + dtoverlay_error("not a valid FDT - err %d", err); + goto error_exit; + } + + dtb_len = fdt_totalsize(fdt); + + if (buf_size < dtb_len) + { + dtoverlay_error("fdt is too large"); + err = -FDT_ERR_NOSPACE; + goto error_exit; + } + + if (buf_size > dtb_len) + fdt_set_totalsize(fdt, buf_size); + + dtb = calloc(1, sizeof(DTBLOB_T)); + if (!dtb) + { + dtoverlay_error("out of memory"); + goto error_exit; + } + + dtb->fdt = fdt; + dtb->max_phandle = 0; // Not a valid phandle + + // Find the minimum and maximum phandles, in case it is necessary to + // relocate existing ones or create new ones. + + for (node_off = 0; + node_off >= 0; + node_off = fdt_next_node(fdt, node_off, NULL)) + { + uint32_t phandle = fdt_get_phandle(fdt, node_off); + if (phandle > dtb->max_phandle) + dtb->max_phandle = phandle; + } + +error_exit: + return dtb; +} + +int dtoverlay_save_dtb(const DTBLOB_T *dtb, const char *filename) +{ + FILE *fp = fopen(filename, "wb"); + int err = 0; + + if (fp) + { + long len = fdt_totalsize(dtb->fdt); + if (len != fwrite(dtb->fdt, 1, len, fp)) + { + dtoverlay_error("fwrite failed"); + err = -2; + goto error_exit; + } + if (dtb->trailer_len && + (fwrite(dtb->trailer, 1, dtb->trailer_len, fp) != dtb->trailer_len)) + { + dtoverlay_error("fwrite failed"); + err = -2; + goto error_exit; + } + + dtoverlay_debug("Wrote %ld bytes to '%s'", len, filename); + fclose(fp); + } + else + { + dtoverlay_debug("Failed to create '%s'", filename); + err = -1; + } + +error_exit: + return err; +} + +int dtoverlay_extend_dtb(DTBLOB_T *dtb, int new_size) +{ + int size = fdt_totalsize(dtb->fdt); + int err = 0; + + if (new_size < 0) + new_size = size - new_size; + + if (new_size > size) + { + void *fdt; + fdt = malloc(new_size); + if (fdt) + { + memcpy(fdt, dtb->fdt, size); + fdt_set_totalsize(fdt, new_size); + + if (dtb->fdt_is_malloced) + free(dtb->fdt); + + dtb->fdt = fdt; + dtb->fdt_is_malloced = 1; + } + else + { + err = -FDT_ERR_NOSPACE; + } + } + else if (new_size < size) + { + /* Can't shrink it */ + err = -FDT_ERR_NOSPACE; + } + + return err; +} + +int dtoverlay_dtb_totalsize(DTBLOB_T *dtb) +{ + return fdt_totalsize(dtb->fdt); +} + +void dtoverlay_pack_dtb(DTBLOB_T *dtb) +{ + fdt_pack(dtb->fdt); +} + +void dtoverlay_free_dtb(DTBLOB_T *dtb) +{ + if (dtb) + { + if (dtb->fdt_is_malloced) + free(dtb->fdt); + if (dtb->trailer_is_malloced) + free(dtb->trailer); + free(dtb); + } +} + +int dtoverlay_find_phandle(DTBLOB_T *dtb, int phandle) +{ + return fdt_node_offset_by_phandle(dtb->fdt, phandle); +} + +int dtoverlay_find_symbol(DTBLOB_T *dtb, const char *symbol_name) +{ + int symbols_off, path_len; + const char *node_path; + + node_path = dtoverlay_get_alias(dtb, symbol_name); + + if (node_path) + { + path_len = strlen(node_path); + } + else + { + symbols_off = fdt_path_offset(dtb->fdt, "/__symbols__"); + + if (symbols_off < 0) + { + dtoverlay_error("No symbols found"); + return -FDT_ERR_NOTFOUND; + } + + node_path = fdt_getprop(dtb->fdt, symbols_off, symbol_name, &path_len); + if (path_len < 0) + return -FDT_ERR_NOTFOUND; + } + return fdt_path_offset_namelen(dtb->fdt, node_path, path_len); +} + +int dtoverlay_find_matching_node(DTBLOB_T *dtb, const char **node_names, + int pos) +{ + while (1) + { + const char *node_name; + pos = fdt_next_node(dtb->fdt, pos, NULL); + if (pos < 0) + break; + node_name = fdt_get_name(dtb->fdt, pos, NULL); + if (node_name) + { + int i; + for (i = 0; node_names[i]; i++) + { + const char *node = node_names[i]; + int matchlen = strlen(node); + if ((strncmp(node_name, node, matchlen) == 0) && + ((node[matchlen] == '\0') || + (node[matchlen] == '@'))) + return pos; + } + } + } + return -1; +} + +int dtoverlay_node_is_enabled(DTBLOB_T *dtb, int pos) +{ + if (pos >= 0) + { + const void *prop = dtoverlay_get_property(dtb, pos, "status", NULL); + if (prop && + ((strcmp((const char *)prop, "okay") == 0) || + (strcmp((const char *)prop, "ok") == 0))) + return 1; + } + return 0; +} + +const void *dtoverlay_get_property(DTBLOB_T *dtb, int pos, const char *prop_name, int *prop_len) +{ + return fdt_getprop(dtb->fdt, pos, prop_name, prop_len); +} + +int dtoverlay_set_property(DTBLOB_T *dtb, int pos, + const char *prop_name, const void *prop, int prop_len) +{ + int err = fdt_setprop(dtb->fdt, pos, prop_name, prop, prop_len); + if (err < 0) + dtoverlay_error("Failed to set property '%s'", prop_name); + return err; +} + +const char *dtoverlay_get_alias(DTBLOB_T *dtb, const char *alias_name) +{ + int node_off; + int prop_len; + const char *alias; + + node_off = fdt_path_offset(dtb->fdt, "/aliases"); + + alias = fdt_getprop(dtb->fdt, node_off, alias_name, &prop_len); + if (alias && !prop_len) + alias = ""; + return alias; +} + +int dtoverlay_set_alias(DTBLOB_T *dtb, const char *alias_name, const char *value) +{ + int node_off; + + node_off = fdt_path_offset(dtb->fdt, "/aliases"); + + return fdt_setprop_string(dtb->fdt, node_off, alias_name, value); +} + +void dtoverlay_set_logging_func(DTOVERLAY_LOGGING_FUNC *func) +{ + dtoverlay_logging_func = func; +} + +void dtoverlay_enable_debug(int enable) +{ + dtoverlay_debug_enabled = enable; +} + +void dtoverlay_error(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + (*dtoverlay_logging_func)(DTOVERLAY_ERROR, fmt, args); + va_end(args); +} + +void dtoverlay_debug(const char *fmt, ...) +{ + va_list args; + if (dtoverlay_debug_enabled) + { + va_start(args, fmt); + (*dtoverlay_logging_func)(DTOVERLAY_DEBUG, fmt, args); + va_end(args); + } +} + +static void dtoverlay_stdio_logging(dtoverlay_logging_type_t type, + const char *fmt, va_list args) +{ + const char *type_str; + + switch (type) + { + case DTOVERLAY_ERROR: + type_str = "error"; + break; + + case DTOVERLAY_DEBUG: + type_str = "debug"; + break; + + default: + type_str = "?"; + } + + fprintf(stderr, "DTOVERLAY[%s]: ", type_str); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); +} diff --git a/helpers/dtoverlay/dtoverlay.h b/helpers/dtoverlay/dtoverlay.h new file mode 100755 index 0000000..4602114 --- /dev/null +++ b/helpers/dtoverlay/dtoverlay.h @@ -0,0 +1,197 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef DTOVERLAY_H +#define DTOVERLAY_H + +#define BE4(x) ((x)>>24)&0xff, ((x)>>16)&0xff, ((x)>>8)&0xff, ((x)>>0)&0xff +#define GETBE4(p, off) ((((unsigned char *)p)[off + 0]<<24) + (((unsigned char *)p)[off + 1]<<16) + \ + (((unsigned char *)p)[off + 2]<<8) + (((unsigned char *)p)[off + 3]<<0)) +#define SETBE4(p, off, x) do { \ + ((unsigned char *)p)[off + 0] = ((x>>24) & 0xff); \ + ((unsigned char *)p)[off + 1] = ((x>>16) & 0xff); \ + ((unsigned char *)p)[off + 2] = ((x>>8) & 0xff); \ + ((unsigned char *)p)[off + 3] = ((x>>0) & 0xff); \ +} while (0) + +#define NON_FATAL(err) (((err) < 0) ? -(err) : (err)) +#define IS_FATAL(err) ((err) < 0) +#define ONLY_FATAL(err) (IS_FATAL(err) ? (err) : 0) + +#define DTOVERLAY_PADDING(size) (-(size)) + +typedef enum +{ + DTOVERLAY_ERROR, + DTOVERLAY_DEBUG +} dtoverlay_logging_type_t; + +typedef struct dtoverlay_struct +{ + const char *param; + int len; + const char *b; +} DTOVERLAY_PARAM_T; + +typedef struct dtblob_struct +{ + void *fdt; + char fdt_is_malloced; + char trailer_is_malloced; + char fixups_applied; + int min_phandle; + int max_phandle; + void *trailer; + int trailer_len; +} DTBLOB_T; + + +typedef void DTOVERLAY_LOGGING_FUNC(dtoverlay_logging_type_t type, + const char *fmt, va_list args); + +typedef int (*override_callback_t)(int override_type, + DTBLOB_T *dtb, int node_off, + const char *prop_name, int target_phandle, + int target_off, int target_size, + void *callback_value); + +/* Return values: -ve = fatal error, positive = non-fatal error */ +int dtoverlay_create_node(DTBLOB_T *dtb, const char *node_name, int path_len); + +int dtoverlay_delete_node(DTBLOB_T *dtb, const char *node_name, int path_len); + +int dtoverlay_find_node(DTBLOB_T *dtb, const char *node_path, int path_len); + +int dtoverlay_set_node_properties(DTBLOB_T *dtb, const char *node_path, + DTOVERLAY_PARAM_T *properties, + unsigned int num_properties); + +int dtoverlay_create_prop_fragment(DTBLOB_T *dtb, int idx, int target_phandle, + const char *prop_name, const void *prop_data, + int prop_len); + +int dtoverlay_fixup_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb); + +int dtoverlay_merge_overlay(DTBLOB_T *base_dtb, DTBLOB_T *overlay_dtb); + +int dtoverlay_merge_params(DTBLOB_T *dtb, const DTOVERLAY_PARAM_T *params, + unsigned int num_params); + +const char *dtoverlay_find_override(DTBLOB_T *dtb, const char *override_name, + int *data_len); + +int dtoverlay_override_one_target(int override_type, + DTBLOB_T *dtb, int node_off, + const char *prop_name, int target_phandle, + int target_off, int target_size, + void *callback_value); + +int dtoverlay_foreach_override_target(DTBLOB_T *dtb, const char *override_name, + const char *override_data, int data_len, + override_callback_t callback, + void *callback_value); + +int dtoverlay_apply_override(DTBLOB_T *dtb, const char *override_name, + const char *override_data, int data_len, + const char *override_value); + +int dtoverlay_override_one_target(int override_type, + DTBLOB_T *dtb, int node_off, + const char *prop_name, int target_phandle, + int target_off, int target_size, + void *callback_value); + +int dtoverlay_set_synonym(DTBLOB_T *dtb, const char *dst, const char *src); + +int dtoverlay_dup_property(DTBLOB_T *dtb, const char *node_name, + const char *dst, const char *src); + +DTBLOB_T *dtoverlay_create_dtb(int max_size); + +DTBLOB_T *dtoverlay_load_dtb_from_fp(FILE *fp, int max_size); + +DTBLOB_T *dtoverlay_load_dtb(const char *filename, int max_size); + +DTBLOB_T *dtoverlay_import_fdt(void *fdt, int max_size); + +int dtoverlay_save_dtb(const DTBLOB_T *dtb, const char *filename); + +int dtoverlay_extend_dtb(DTBLOB_T *dtb, int new_size); + +int dtoverlay_dtb_totalsize(DTBLOB_T *dtb); + +void dtoverlay_pack_dtb(DTBLOB_T *dtb); + +void dtoverlay_free_dtb(DTBLOB_T *dtb); + +static inline void *dtoverlay_dtb_trailer(DTBLOB_T *dtb) +{ + return dtb->trailer; +} + +static inline int dtoverlay_dtb_trailer_len(DTBLOB_T *dtb) +{ + return dtb->trailer_len; +} + +static inline void dtoverlay_dtb_set_trailer(DTBLOB_T *dtb, + void *trailer, + int trailer_len) +{ + dtb->trailer = trailer; + dtb->trailer_len = trailer_len; + dtb->trailer_is_malloced = 0; +} + +int dtoverlay_find_phandle(DTBLOB_T *dtb, int phandle); + +int dtoverlay_find_symbol(DTBLOB_T *dtb, const char *symbol_name); + +int dtoverlay_find_matching_node(DTBLOB_T *dtb, const char **node_names, + int pos); + +int dtoverlay_node_is_enabled(DTBLOB_T *dtb, int pos); + +const void *dtoverlay_get_property(DTBLOB_T *dtb, int pos, + const char *prop_name, int *prop_len); + +int dtoverlay_set_property(DTBLOB_T *dtb, int pos, + const char *prop_name, const void *prop, int prop_len); + +const char *dtoverlay_get_alias(DTBLOB_T *dtb, const char *alias_name); + +int dtoverlay_set_alias(DTBLOB_T *dtb, const char *alias_name, const char *value); + +void dtoverlay_set_logging_func(DTOVERLAY_LOGGING_FUNC *func); + +void dtoverlay_enable_debug(int enable); + +void dtoverlay_error(const char *fmt, ...); + +void dtoverlay_debug(const char *fmt, ...); + +#endif diff --git a/helpers/v3d/v3d_common.h b/helpers/v3d/v3d_common.h new file mode 100755 index 0000000..61002b1 --- /dev/null +++ b/helpers/v3d/v3d_common.h @@ -0,0 +1,39 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef V3D_COMMON_H +#define V3D_COMMON_H + +#include "helpers/v3d/v3d_ver.h" +#include "interface/vcos/vcos_assert.h" +#include "interface/vcos/vcos_stdbool.h" +#include "interface/vcos/vcos_stdint.h" +#include "interface/vcos/vcos_types.h" /* for vcos_unused and VCOS_STATIC_INLINE */ + +typedef uint32_t V3D_ADDR_T; +typedef uint16_t float16_t; + +#endif diff --git a/helpers/v3d/v3d_ver.h b/helpers/v3d/v3d_ver.h new file mode 100755 index 0000000..967e55c --- /dev/null +++ b/helpers/v3d/v3d_ver.h @@ -0,0 +1,66 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef V3D_VER_H +#define V3D_VER_H + +/* if V3D_TECH_VERSION/V3D_REVISION aren't defined, try to figure them out from + * other defines... */ +#ifndef V3D_TECH_VERSION + #define V3D_TECH_VERSION 2 +#endif +#ifndef V3D_REVISION + #ifdef __BCM2708A0__ + #ifdef HERA + /* bcg a1, hera */ + #define V3D_REVISION 1 + #else + /* 2708 a0 */ + #define V3D_REVISION 0 + #endif + #elif defined(__BCM2708B0__) + /* 2708 b0 */ + #define V3D_REVISION 2 + #elif defined(__BCM2708C0__) || defined(__BCM2708C1__) + /* 2708 c0/1 */ + #define V3D_REVISION 3 + #else + /* capri */ + #define V3D_REVISION 6 + #endif +#endif + +#define V3D_MAKE_VER(TECH_VERSION, REVISION) (((TECH_VERSION) * 100) + (REVISION)) +#define V3D_VER (V3D_MAKE_VER(V3D_TECH_VERSION, V3D_REVISION)) +#define V3D_VER_AT_LEAST(TECH_VERSION, REVISION) (V3D_VER >= V3D_MAKE_VER(TECH_VERSION, REVISION)) + +/* TODO this is temporary */ +#if V3D_VER == V3D_MAKE_VER(9, 9) +#define V3D_VER_D3D +#endif + +#endif diff --git a/helpers/vc_image/metadata_fourcc.h b/helpers/vc_image/metadata_fourcc.h new file mode 100755 index 0000000..64bccf7 --- /dev/null +++ b/helpers/vc_image/metadata_fourcc.h @@ -0,0 +1,107 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _METADATA_FOURCC_H +#define _METADATA_FOURCC_H + +/****************************************************************************** +Definition of 4CCs assigned to metadata items. This list acts more as a +central repository of 4CCs for different metadata items so as to avoid clashes. +They are not otherwise necessary for library to function correctly. +******************************************************************************/ + +/* + Note: Multi-character constants are not valid C and although supported by + some compilers such as Metaware, they are ambiguous in how they should be + packed into a long due to endian-ness (and also due to size for 2 and 3 + character constants). +*/ + +typedef enum { + + METADATA_CDI = ('C'<<24)+('D'<<16)+('M'<<8)+('D'), // 'CDMD' struct CDI_METADATA_T in /middleware/camplus/cdi/cdi.h + METADATA_CAM_CAL = ('C'<<24)+('C'<<16)+('A'<<8)+('L'), // 'CCAL' struct CAMERA_CALIBRATION_METADATA_T in /middleware/camplus/cdi/cdi.h + METADATA_TIMING = ('T'<<24)+('I'<<16)+('M'<<8)+('E'), // 'TIME' struct TIMING_METADATA_T in /middleware/camplus/cdi/cdi.h + METADATA_CDI_FILE = ( 0 <<24)+('C'<<16)+('D'<<8)+('F'), // '\x00CDF' struct CDI_FILE_METADATA_T in /middleware/camplus/cdi/cdi_file.h + METADATA_CDI_RAW = ('C'<<24)+('D'<<16)+('R'<<8)+('W'), // 'CDRW' struct CDI_FILE_RAW_METADATA_T in /middleware/camplus/cdi/cdi_file_raw.h + METADATA_STABILISE = ('S'<<24)+('T'<<16)+('A'<<8)+('B'), // 'STAB' struct GLOBAL_MOTION_VECTOR_T in /middleware/camplus/sw/stabilise/stabilise.h + METADATA_LOCAL_MV1 = ('L'<<24)+('M'<<16)+('V'<<8)+('1'), // 'LMV1' struct ALL_LOCAL_MOTION_VECTORS_T in /middleware/camplus/sw/stabilise/stabilise.h + METADATA_LOCAL_MV2 = ('L'<<24)+('M'<<16)+('V'<<8)+('2'), // 'LMV2' struct ALL_LOCAL_MOTION_VECTORS_T in /middleware/camplus/sw/stabilise/stabilise.h + METADATA_TUNER_AGC = ( 0 <<24)+('A'<<16)+('G'<<8)+('C'), // '\x00AGC' struct ISP_AGC_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h + METADATA_AGC_DEBUG = ('A'<<24)+('G'<<16)+('C'<<8)+('D'), // 'AGCD' struct ISP_TUNER_BRCM_AGC_DEBUG_T in /middleware/ISP/tuner/isp_tuner_agc.h + METADATA_FOCUS_REGION = ('F'<<24)+('R'<<16)+('G'<<8)+('N'), // 'FRGN' struct ISP_TUNER_BRCM_AF_STATISTICS_PARAMS_T in /middleware/ISP/tuner/isp_tuner_brcm_common.h + METADATA_FOCUS_WOI = ('F'<<24)+('W'<<16)+('O'<<8)+('I'), // 'FWOI' struct ISP_WOI_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h + METADATA_FOCUS_CAF = ('F'<<24)+('C'<<16)+('A'<<8)+('F'), // 'FCAF' struct ISP_CAF_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h + METADATA_AUTOFOCUS = ( 0 <<24)+( 0 <<16)+('A'<<8)+('F'), // '\x00\x00AF' struct ISP_AF_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h + METADATA_EV = ('E'<<24)+('V'<<16)+('M'<<8)+('D'), // 'EVMD' struct ISP_TUNER_BRCM_EV_METADATA_T in /middleware/ISP/tuner/isp_tuner_brcm_common.h + METADATA_ISP = ('I'<<24)+('S'<<16)+('P'<<8)+('M'), // 'ISPM' struct ISP_ISP_METADATA_T in /middleware/ISP/tuner/isp_tuner_ctrl.h + METADATA_FACETRACKING = ('F'<<24)+('A'<<16)+('C'<<8)+('E'), // 'FACE' struct FACE_METADATA_T defined in /middleware/camplus/sw/face_metadata.h + METADATA_ANTISHAKE = ('S'<<24)+('H'<<16)+('A'<<8)+('K'), // 'SHAK' struct SHAKE_METADATA_T defined in /middleware/camplus/sw/shake_metadata.h + METADATA_RER = ( 0 <<24)+('R'<<16)+('E'<<8)+('R'), // '\x00RER' struct RER_METADATA_T defined in /middleware/camplus/sw/rer_metadata.h + METADATA_SCENEDETECTION = ( 0 <<24)+('A'<<16)+('S'<<8)+('D'), // '\x00ASD' struct ASD_METADATA_T defined in /middleware/camplus/sw/asd_metadata.h + METADATA_TUNER_SYNC = ('S'<<24)+('Y'<<16)+('N'<<8)+('C'), // 'SYNC' NULL data, just adds the item header. + METADATA_DARK_FRAME_CORRECT = ('D'<<24)+('F'<<16)+('R'<<8)+('C'), // 'DFRC' 5 byte literal string "dfrc" + METADATA_DARK_FRAME_SUB = ('D'<<24)+('F'<<16)+('S'<<8)+('B'), // 'DFSB' 3 byte literal string "on" + METADATA_TUNER_DROP_FRAME = ('T'<<24)+('D'<<16)+('R'<<8)+('P'), + METADATA_ABL = ( 0 <<24)+('A'<<16)+('B'<<8)+('L'), // '\x00ABL' struct ISP_TUNER_BRCM_BLACK_LEVEL_ABL_T defined in /middleware/ISP/tuner/isp_tuner_brcm_black_level.h + METADATA_DRC = ( 0 <<24)+('D'<<16)+('R'<<8)+('C'), // 'DRC' struct DRC_METADATA_T defined in /middleware/camplus/sw/drc/drc.h + METADATA_REGISTRATION = ( 0 <<24)+('R'<<16)+('E'<<8)+('G'), // 'REG' struct REGISTRATION_OFFSETS_T defined in /middleware/camplus/sw/registration/registration.h + METADATA_RAW_CAPTURE = ('R'<<24)+('W'<<16)+('M'<<8)+('D'), // 'RWMD' struct RAW_CAPTURE_METADATA_T defined in /middleware/camplus/sw/raw_metadata.h + // structure definitions for IL metadata are + // in middleware/openmaxil/headers/ilmetadata.h + METADATA_IL_CAMERA_NAME = ('I'<<24)+('L'<<16)+('C'<<8)+('A'), // 'ILCA' + METADATA_IL_CROP_RECTANGLE = ('I'<<24)+('L'<<16)+('C'<<8)+('R'), // 'ILCR' + METADATA_IL_PIXEL_ASPECT_RATIO = ('I'<<24)+('L'<<16)+('P'<<8)+('A'), // 'ILPA' + + METADATA_TUNER_FLASH_MONITOR = ('F'<<24)+('L'<<16)+('M'<<8)+('N'), // 'FLMN' Flash monitor - type ISP_TUNER_BRCM_FLASH_MONITOR_T from isp_tuner_brcm.h + + + // VoWIFI video analysis + METADATA_SPATIAL_ACT_A = ( 'S'<<24)+('P'<<16)+('T'<<8)+('A'), // 'SPTA' : SPATIAL_ACTIVITY_METADATA_T defined in /middleware/camplus/sw/perceptual/spatial_activity.h + METADATA_TEMPORAL_ACT_A = ( 'T'<<24)+('M'<<16)+('P'<<8)+('A'), // 'TMPA' : TEMPORAL_ACTIVITY_METADATA_T defined in /middleware/camplus/sw/perceptual/temporal_activity.h + METADATA_FILMGRAIN_NOISE_A = ( 'F'<<24)+('G'<<16)+('N'<<8)+('A'), // 'FGNA' : FILMGRAIN_NOISE_METADATA_T defined in /middleware/camplus/sw/perceptual/filmgrain_noise.h + METADATA_COLORSPACE_A = ( 'C'<<24)+('L'<<16)+('R'<<8)+('A'), // 'CLRA' : COLORSPACE_METADATA_T defined in /middleware/camplus/sw/perceptual/colorspace.h + METADATA_FLAT_AREA_A = ( 'F'<<24)+('L'<<16)+('T'<<8)+('A'), // 'FLTA' : FLAT_AREA_METADATA_T defined in /middleware/camplus/sw/perceptual/flatarea.h + METADATA_STILL_AREA_A = ( 'S'<<24)+('T'<<16)+('L'<<8)+('A'), // 'STLA' : FLAT_AREA_METADATA_T defined in /middleware/camplus/sw/perceptual/stillarea.h + METADATA_DDITHER_A = ( 'D'<<24)+('D'<<16)+('T'<<8)+('A'), // 'DDTA' : DDITHER_METADATA_T defined in /middleware/camplus/sw/perceptual/... + + METADATA_LINKED_MULTICHANN = ( 'I'<<24)+('L'<<16)+('M'<<8)+('C'), // 'ILMC' : VC_IMAGE_LINKED_MULTICHANN_T defined in /helpers/vc_image/vc_image.h + + METADATA_HDR = ( 0 <<24)+( 'H'<<16)+('D'<<8)+('R'), // 'HDR' : HDR_METADATA_T defined in /middleware/camplus/sw/hdr/hdr_metadata.h + METADATA_FOCUS_STATS_PREPROC = ('F'<<24)+('S'<<16)+('P'<<8)+('M'), // 'FSPM' : FOCUS_STATS_PREPROC_METADATA defined in /middleware/camplus/sw/hdr/focus_stats_preproc/focus_stats_preproc.h + METADATA_ACUTE_AWB_LOG = ('A'<<24)+('E'<<16)+('L'<<8)+('C'), // 'AELC' : ISP_ACUTE_AWB_LOG + METADATA_DF = ( 0 <<24)+( 0<<16)+('D'<<8)+('F'), // '\x00\x00DF' : DF_METADATA_T defined in /middleware/camplus/sw/df/df_metadata.h + METADATA_MAGIC_MEASURE = ('S'<<24)+('S'<<16)+('M'<<8) + ('M'), // 'SSMM' : A statistic from the ISP used to determine the JPEG quality setting for a certain customer. + METADATA_SNAPSHOT_JPEG_QUANTISER = ('S'<<24)+('S'<<16)+('J'<<8) + ('S'), // 'SSJQ' : The size of the snapshot frame when JPEG-encoded. + + METADATA_SUPPLEMENTARY_INFO = ('S'<<24)+('U'<<16)+('P'<<8) + ('P'), // 'SUPP' : Supplimentary info defined in /codecs/video/hw/enc/venc_supplementary_info.h + + METADATA_UNKNOWN = ('U'<<24)+('N'<<16)+('K'<<8)+('N') // 'UNKN' + +} METADATA_CODE_T; + +#endif diff --git a/helpers/vc_image/vc_image.h b/helpers/vc_image/vc_image.h new file mode 100755 index 0000000..f566348 --- /dev/null +++ b/helpers/vc_image/vc_image.h @@ -0,0 +1,804 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file + * + * This file describes the public interfaces to the old vc_image library. + */ + +#ifndef VC_IMAGE_H +#define VC_IMAGE_H + +#if !defined(__VIDEOCORE__) && !defined(__vce__) && !defined(VIDEOCORE_CODE_IN_SIMULATION) +#error This code is only for use on VideoCore. If it is being included +#error on the host side, then you have done something wrong. Defining +#error __VIDEOCORE__ is *not* the right answer! +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Definition for VC_IMAGE_TYPE_T live in here */ +#include "vcinclude/vc_image_types.h" +#include "helpers/vc_image/vc_image_helper.h" + +#if !defined __SYMBIAN32__ + +#if !defined( _MSC_VER) && !defined(__WIN32__) + #include "vcfw/rtos/rtos.h" +#endif +#include "vcfw/rtos/common/rtos_common_mem.h" + +#include "helpers/vc_image/vc_image_metadata.h" + +#if defined(__VIDEOCORE3__) && !defined(RGB888) +#ifdef __GNUC__ +#warning "RGB888 should be defined on all VideoCore3 platforms, please check your platform makefile." +#else +# warn "RGB888 should be defined on all VideoCore3 platforms, please check your platform makefile." +#endif +#endif + + /* Types of extra properties that can be passed to vc_image_configure() */ + + typedef enum + { + VC_IMAGE_PROP_END = 0, + VC_IMAGE_PROP_NONE = 0x57EAD400, + VC_IMAGE_PROP_ALIGN, + VC_IMAGE_PROP_STAGGER, + VC_IMAGE_PROP_PADDING, + VC_IMAGE_PROP_PRIORITY, + VC_IMAGE_PROP_MALLOCFN, + VC_IMAGE_PROP_CACHEALIAS, + VC_IMAGE_PROP_CLEARVALUE, + + /* insert only allocation-related properties above this line */ + + VC_IMAGE_PROP_REF_IMAGE = 0x57EAD47f, + VC_IMAGE_PROP_ROTATED, + VC_IMAGE_PROP_PITCH, + VC_IMAGE_PROP_DATA, + VC_IMAGE_PROP_SIZE, + VC_IMAGE_PROP_PALETTE, + VC_IMAGE_PROP_MIPMAPS, + VC_IMAGE_PROP_BAYER_ORDER, + VC_IMAGE_PROP_BAYER_FORMAT, + VC_IMAGE_PROP_BAYER_BLOCK_SIZE, + VC_IMAGE_PROP_CODEC_FOURCC, + VC_IMAGE_PROP_CODEC_MAXSIZE, + VC_IMAGE_PROP_CUBEMAP, + VC_IMAGE_PROP_OPENGL_FORMAT_AND_TYPE, + VC_IMAGE_PROP_INFO, + VC_IMAGE_PROP_METADATA, + VC_IMAGE_PROP_NEW_LIST, + /* Multi-channel properties */ + VC_IMAGE_PROP_NUM_CHANNELS, + VC_IMAGE_PROP_IS_TOP_BOTTOM, + VC_IMAGE_PROP_IS_DECIMATED, + VC_IMAGE_PROP_IS_PACKED, + VC_IMAGE_PROP_YUV_COLOURSPACE, + /* Linked-multichannel properties*/ + VC_IMAGE_PROP_LINKED_MULTICHANN, + VC_IMAGE_PROP_VPITCH + } VC_IMAGE_PROPERTY_T; + + /* A property key and value */ + typedef struct + { + VC_IMAGE_PROPERTY_T prop; + unsigned long value; + } VC_IMAGE_PROPLIST_T; +#define VC_IMAGE_PROPLIST_COUNT(x) (sizeof(x)/sizeof(x[0])) +#define VC_IMAGE_PROPLIST_NULL ((VC_IMAGE_PROPLIST_T*)0) + + + /* Useful bits that can be set, to check validity we'll assume that all other + bits should be zero and enforce this in vc_image functions to catch people + who aren't initialising the VC_IMAGE_T structure nicely; update when other + bits are added */ +#define VC_IMAGE_INFO_VALIDBITS 0x9F0F + /* Define the bits of info used to denote the colourspace */ +#define VC_IMAGE_INFO_COLOURSPACE 0x0F + +#endif //#if !defined __SYMBIAN32__ + +#define is_valid_vc_image_hndl(_img, _type) ((_img) != NULL \ + && (_img)->image_data == NULL \ + && ((_img)->info.info & ~VC_IMAGE_INFO_VALIDBITS) == 0 \ + && (_type) > VC_IMAGE_MIN && (_type) < VC_IMAGE_MAX \ + && (_img)->type == (_type) \ + ) + +#define is_valid_vc_image_buf(_img, _type) ((_img) != NULL \ + && (_img)->image_data != NULL \ + && ((_img)->info.info & ~VC_IMAGE_INFO_VALIDBITS) == 0 \ + && (_type) > VC_IMAGE_MIN && (_type) < VC_IMAGE_MAX \ + && (_img)->type == (_type) \ + ) + +#define is_within_rectangle(x, y, w, h, cont_x, cont_y, cont_w, cont_h) ( \ + (x) >= (cont_x) \ + && (y) >= (cont_y) \ + && ((x) + (w)) <= ((cont_x) + (cont_w)) \ + && ((y) + (h)) <= ((cont_y) + (cont_h)) \ + ) + + +#if !defined __SYMBIAN32__ + + + /** + * \brief storage for pointers to image headers of the previous and next channel + * + * + * When linked-multichannel structure is being used, this structure stores the pointers + * necessary to link the current image header in to make represent a multichannel image. + */ + struct VC_IMAGE_LINKED_MULTICHANN_T { + VC_IMAGE_T* prev; + VC_IMAGE_T* next; + }; + typedef struct VC_IMAGE_LINKED_MULTICHANN_T VC_IMAGE_LINKED_MULTICHANN_T; + + /* Point type for use in polygon drawing. */ + typedef struct vc_image_point_s { + int x, y; + } VC_IMAGE_POINT_T; + + /* Useful for rectangular crop regions in a vc_image */ + typedef struct vc_image_rect_s + { + unsigned int x, y; + unsigned int width; + unsigned int height; + } VC_IMAGE_RECT_T; + + /* CRC type for the return of CRC results */ + typedef struct VC_IMAGE_CRC_T_ { + unsigned int y; + unsigned int uv; + } VC_IMAGE_CRC_T; + + /* To make stack-based initialisation convenient: */ + +#define NULL_VC_IMAGE_T {0, 0, 0, 0, 0, 0} + + /* Convenient constants that can be passed more readably to vc_image_text. */ + +#define VC_IMAGE_COLOUR_TRANSPARENT -1 +#define VC_IMAGE_COLOUR_FADE -2 + +#define CLIP_DEST(x_offset, y_offset, width, height, dest_width, dest_height) \ + if (x_offset<0) \ + width-=-x_offset, x_offset=0; \ + if (y_offset<0) \ + height-=-y_offset, y_offset=0; \ + if (x_offset+width>dest_width) \ + width-=x_offset+width-dest_width; \ + if (y_offset+height>dest_height) \ + height-=y_offset+height-dest_height; \ + + + /* Publicly exported functions. */ + + /****************************************************************************** + General functions. + ******************************************************************************/ + +//routine to return a bitmask (bit referenced by VC_IMAGE_TYPE_T) of the supported image formats built in +//this particular version of the vc_image library + unsigned int vc_image_get_supported_image_formats( void ); + + /****************************************************************************** + Data member access. + ******************************************************************************/ + + /* Initialise all fields of a VC_IMAGE_T to zero. */ + + void vc_image_initialise(VC_IMAGE_T *image); + + /* Return specified channel of a multi-channel VC_IMAGE_T as a single-channel VC_IMAGE_T. */ + + void vc_image_extract_channel(VC_IMAGE_T *extract, VC_IMAGE_T *image, uint8_t chan_idx); + + /* Fill out all fields of a VC_IMAGE_T in the same way as vc_image_malloc(), but non-allocating. */ + +#if defined(va_start) /* can't publish this without including */ + int vc_image_vconfigure(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, va_list proplist); + int vc_image_vreconfigure(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, va_list proplist); +#endif + int vc_image_configure_unwrapped(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, VC_IMAGE_PROPERTY_T prop, ...); + int vc_image_reconfigure_unwrapped(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, VC_IMAGE_PROPERTY_T prop, ...); + /* remaining arguments are a VC_IMAGE_PROPERTY_T list terminated with VC_IMAGE_END */ + + int vc_image_configure_proplist(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, long width, long height, VC_IMAGE_PROPLIST_T *props, unsigned long n_props); + + void vc_image_lock( VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *src ); + + void vc_image_lock_extract( VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *src, uint8_t chan_idx ); + + void vc_image_lock_channel(VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *chann); + void vc_image_lock_channel_perma(VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *chann); //lightweight version of lock channel + + void vc_image_unlock( VC_IMAGE_BUF_T *img ); + + void vc_image_lock_perma( VC_IMAGE_BUF_T *dst, const VC_IMAGE_T *src ); + + void vc_image_unlock_perma( VC_IMAGE_BUF_T *img ); + + /* Mipmap-related functions. Image must be brcm1. */ + + /* Take the base image of an image and scale it down into its mipmaps. */ + /* The filter argument type is likely to change, but 0 should always be a + * reasonable value (even if it becomes a pointer). */ + void vc_image_rebuild_mipmaps(VC_IMAGE_BUF_T *dst, int filter); + + /****************************************************************************** + Image/bitmap manipulation. + ******************************************************************************/ + + /* Fill a region of an image with solid colour. */ + + void vc_image_fill(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, int value); + + /* Blt a region of an image to another. */ + + void vc_image_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Blt a region of an image to another, changing the format on the fly. */ + + void vc_image_convert_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Expose the special cases of above */ + void vc_image_yuv420_to_rgba32_part(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T const *src, int src_x_offset, int src_y_offset, int alpha); + + void vc_image_rgb565_to_rgba32_part(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, VC_IMAGE_BUF_T const *src, int src_x_offset, int src_y_offset, int alpha); + /* Blt a region of an image to another with a nominated transparent colour. */ + + void vc_image_transparent_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int transparent_colour); + + /* Blt a region of an image to another but only overwriting pixels of a given colour. */ + + void vc_image_overwrite_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int overwrite_colour); + + /* Blt a region of an image to another only where a mask bitmap has 1s. */ + + void vc_image_masked_blt (VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, + VC_IMAGE_BUF_T *mask, int mask_x_offset, int mask_y_offset, + int invert); + + /* Masked fill a region with a solid colour. */ + + void vc_image_masked_fill(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, int value, + VC_IMAGE_BUF_T *mask, int mask_x_offset, int mask_y_offset, + int invert); + + /* Blt an image into 3 separate buffers, either YUV or RGB + Will always copy the entire image. In the case of RGB image: + Y = RGB, U = NULL, V = NULL */ + + void vc_image_fetch(VC_IMAGE_BUF_T *src, unsigned char* Y, unsigned char* U, unsigned char* V); + + /* Blt the data in Y U V (or RGB) buffers into a VC_IMAGE structure + Opposite to vc_image_put above */ + + void vc_image_put(VC_IMAGE_BUF_T *src, unsigned char* Y, unsigned char* U, unsigned char* V); + + /* NOT a region of an image. */ + + void vc_image_not(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height); + + /* Fade (or brighten) a region of an image. fade is an 8.8 value. */ + + void vc_image_fade(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, int fade); + + /* OR two images together. */ + + void vc_image_or(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* XOR two images together. */ + + void vc_image_xor(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Copy an image in its entirety (works on 1bpp images). */ + + void vc_image_copy(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); + + /* Transpose an image. */ + + void vc_image_transpose(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); + int vc_image_transpose_ret(VC_IMAGE_T *dest, VC_IMAGE_T *src); + + /* Transpose an image and subsample it by some integer factor */ + void vc_image_transpose_and_subsample(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, int factor); + + /* Vertically flip an image (turn "upside down"). */ + + void vc_image_vflip(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); + + /* Horizontally flip an image ("mirror"). */ + + void vc_image_hflip(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); + + /* Transpose an image in place. Relies on being able to preserve the pitch. */ + + void vc_image_transpose_in_place(VC_IMAGE_BUF_T *dest); + + /* Vertically flip an image (turn "upside down") in place. */ + + void vc_image_vflip_in_place(VC_IMAGE_BUF_T *dest); + + /* Horizontally flip an image ("mirror") in place. */ + + void vc_image_hflip_in_place(VC_IMAGE_BUF_T *dest); + + /* Add text string to an image. */ + + void vc_image_text(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, + int *max_x, int *max_y); + + void vc_image_small_text(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, + int *max_x, int *max_y); + + void vc_image_text_20(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, + int *max_x, int *max_y); + + void vc_image_text_24(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, + int *max_x, int *max_y); + + void vc_image_text_32(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, + int *max_x, int *max_y); + + /* Experimental vector font thingy */ + +#define VC_IMAGE_ATEXT_FIXED (1<<16) // Add this to "size" if you want fixed-width + + int vc_image_atext_measure(int size, const char * str); + + int vc_image_atext_clip_length(int size, const char * str, int width); + + int vc_image_atext(VC_IMAGE_BUF_T * dest, int x, int y, int fg, int bg, int size, const char * str); + + /* Add optionally rotated text to an image */ + + void vc_image_textrotate(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int fg_colour, int bg_colour, const char *text, int rotate); + + + /* Line drawing. */ + + void vc_image_line(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int x_end, int y_end, int fg_colour); + + /* Unfilled Rect. */ + + void vc_image_rect(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int width, int height, int fg_colour); + + /* Add frame . */ + + void vc_image_frame(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int width, int height); + + void vc_image_aa_line(VC_IMAGE_BUF_T *dest, int x_start, int y_start, int x_end, int y_end, int thickness, int colour); + void vc_image_aa_ellipse(VC_IMAGE_BUF_T *dest, int cx, int cy, int a, int b, int thickness, int colour, int filled); + void vc_image_aa_polygon(VC_IMAGE_BUF_T *dest, VC_IMAGE_POINT_T *p, int n, int thickness, int colour, int filled); + + /* Copy and convert image to 48bpp. WARNING: offsets, width and height must be 16-pixel aligned. */ + + void vc_image_convert_to_48bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Copy and convert image from 16bpp to 24bpp. WARNING: offsets, width and height must be 16-pixel aligned. */ + + void vc_image_convert_to_24bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Convert and overlay image from 16bpp to 24bpp with nominated transparent colour. WARNING: offsets, width and height must be 16-pixel aligned. */ + + void vc_image_overlay_to_24bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int transparent_colour); + + /* Copy and convert image from 24bpp to 16bpp. WARNING: offsets, width and height must be 16-pixel aligned. */ + + void vc_image_convert_to_16bpp(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset); + + /* Copy all, or a portion of src to dest with resize. This function is specialized + for YUV images (other cases are not yet coded, and code could be rather large + to link all cases by default). All dimensions should be multiples of 2. + If smooth_flag nonzero, appropriate smoothing will be applied. */ + + void vc_image_resize_yuv(VC_IMAGE_BUF_T *dest, + int dest_x_offset, int dest_y_offset, + int dest_width, int dest_height, + VC_IMAGE_BUF_T *src, + int src_x_offset, int src_y_offset, + int src_width, int src_height, + int smooth_flag); + + /* RGB565 resize. Pitch must be 32-byte aligned, dimensions need not be. + XXX kept YUV and RGB565 version separate to avoid unnecessary linking. + However if we're going down the DLL route they ought to be combined. */ + + void vc_image_resize_rgb565(VC_IMAGE_BUF_T * dest, + int dest_x_offset, int dest_y_offset, + int dest_width, int dest_height, + VC_IMAGE_BUF_T *src, + int src_x_offset, int src_y_offset, + int src_width, int src_height, + int smooth_flag); + + void vc_image_resize(VC_IMAGE_BUF_T *dest, + int dest_x_offset, int dest_y_offset, + int dest_width, int dest_height, + VC_IMAGE_BUF_T *src, + int src_x_offset, int src_y_offset, + int src_width, int src_height, + int smooth_flag); + + /* Resize YUV in strips. XXX this function has rather bizarre arguments. */ + + void vc_image_resize_yuv_strip(VC_IMAGE_BUF_T * dest, VC_IMAGE_BUF_T * src, + int src_x_offset, int src_width, + int pos, int step, int smooth_flag, + void * tmp_buf, int tmp_buf_size); + + /* Blit from a YUV image to an RGB image with optional mirroring followed + by optional clockwise rotation */ + void vc_image_convert(VC_IMAGE_BUF_T *dest,VC_IMAGE_BUF_T *src,int mirror,int rotate); + + /* Convert bitmap from one type to another, doing vertical flip as part of the conversion */ + void vc_image_convert_vflip(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src); + + /* Convert from YUV to RGB, with optional downsample by 2 in each direction. */ + void vc_image_yuv2rgb(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, int downsample); + + void vc_image_convert_yuv2rgb(unsigned char *datay, unsigned char *datau + , unsigned char *datav, int realwidth, int realheight + , unsigned short *buffer, int screenwidth, int screenheight); + + /* Frees up (using free_256bit) the source bytes and vc_image header */ + void vc_image_free(VC_IMAGE_T *img); + + /* Returns an image based on decompressing the given bytes (made by helpers/vc_image/runlength.m) */ + VC_IMAGE_BUF_T *vc_runlength_decode(unsigned char *data); + + /* Returns a 16bit image based on decompressing the given bytes (made by helpers/vc_image/covnertbmp2run.m) */ + VC_IMAGE_BUF_T *vc_run_decode(VC_IMAGE_BUF_T *img,unsigned char *data); + + /* Returns an image based on decompressing the given bytes (made by helpers/vc_image/runlength8.m) */ + VC_IMAGE_BUF_T *vc_runlength_decode8(unsigned char *data); + + /* Returns an image based on decompressing an image (made by helpers/vc_image/enc4by4.m) + Will place the decompressed data into an existing image if supplied */ + VC_IMAGE_BUF_T *vc_4by4_decode(VC_IMAGE_BUF_T *img,unsigned short *data); + + /* Deblocks a YUV image using a simple averaging scheme on 8 by 8 blocks */ + void vc_image_deblock(VC_IMAGE_BUF_T *img); + + /* Pack the bytes (i.e. remove the padding bytes) in a vc_image. */ + void vc_image_pack(VC_IMAGE_BUF_T *img, int x, int y, int w, int h); + + /* Pack bytes as above, but copy them to another memory block in the process. */ + void vc_image_copy_pack (unsigned char *dest, VC_IMAGE_BUF_T *img, int x, int y, int w, int h); + + /* Unpack bytes to have the correct padding. */ + void vc_image_unpack(VC_IMAGE_BUF_T *img, int w, int h); + + /* Unpack bytes as above, but also copy them to another memory block in the process. */ + void vc_image_copy_unpack(VC_IMAGE_BUF_T *img, int dest_x_off, int dest_y_off, unsigned char *src, int w, int h); + + /* swap red/blue */ + void vc_image_swap_red_blue(VC_IMAGE_BUF_T *img); + +#if defined(va_start) /* can't publish this without including */ + VC_IMAGE_BUF_T *vc_image_vparmalloc_unwrapped(VC_IMAGE_TYPE_T type, char const *description, long width, long height, va_list proplist); +#endif + VC_IMAGE_BUF_T *vc_image_parmalloc_unwrapped(VC_IMAGE_TYPE_T type, char const *description, long width, long height, VC_IMAGE_PROPERTY_T prop, ...); + + void vc_image_parfree(VC_IMAGE_BUF_T *p); + + VC_IMAGE_BUF_T *vc_image_malloc( VC_IMAGE_TYPE_T type, long width, long height, int rotate ); + VC_IMAGE_BUF_T *vc_image_prioritymalloc( VC_IMAGE_TYPE_T type, long width, long height, int rotate, int priority, char const *name); +#define vc_image_priorityfree(p) vc_image_free(p) + + VC_IMAGE_T *vc_image_relocatable_alloc(VC_IMAGE_TYPE_T type, const char *description, long width, long height, MEM_FLAG_T flags, VC_IMAGE_PROPERTY_T prop, ...); + void vc_image_relocatable_free(VC_IMAGE_T *img); + + void vc_image_set_palette( short *colourmap ); + short *vc_image_get_palette( VC_IMAGE_T *src ); + + void vc_image_convert_in_place(VC_IMAGE_BUF_T *image, VC_IMAGE_TYPE_T new_type); + + /* Image transformations (flips and 90 degree rotations) */ + VC_IMAGE_TRANSFORM_T vc_image_inverse_transform(VC_IMAGE_TRANSFORM_T transform); + + VC_IMAGE_TRANSFORM_T vc_image_combine_transforms(VC_IMAGE_TRANSFORM_T transform1, VC_IMAGE_TRANSFORM_T transform2); + + void vc_image_transform_point(int *pX, int *pY, int w, int h, VC_IMAGE_TRANSFORM_T transform); + + void vc_image_transform_rect(int *pX, int *pY, int *pW, int *pH, int w, int h, VC_IMAGE_TRANSFORM_T transform); + + void vc_image_transform_dimensions(int *pW, int *pH, VC_IMAGE_TRANSFORM_T transform); + + void vc_image_transform(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, VC_IMAGE_TRANSFORM_T transform); + int vc_image_transform_ret(VC_IMAGE_T *dest, VC_IMAGE_T *src, VC_IMAGE_TRANSFORM_T transform); + + void vc_image_transform_in_place(VC_IMAGE_BUF_T *image, VC_IMAGE_TRANSFORM_T transform); + + void vc_image_transform_brcm1s(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, VC_IMAGE_TRANSFORM_T transform, int hdeci); + + const char *vc_image_transform_string(VC_IMAGE_TRANSFORM_T xfm); + + /* Blt a region of an image to another with a nominated transparent colour and alpha blending. + Alpha should be between 0 and 256 */ + void vc_image_transparent_alpha_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int transparent_colour, int alpha); + + /* Blt a region of an image to another with a nominated transparent colour and alpha blending. + Alpha should be between 0 and 256 */ + void vc_image_transparent_alpha_blt_rotated(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, + VC_IMAGE_BUF_T *src, int transparent_colour, int alpha, int scale, int sin_theta, int cos_theta); + + /* Blt a special 4.4 font image generated from a PNG file. + Only works with a 565 destination image. + Can choose a colour via red,green,blue arguments. Each component should be between 0 and 255. + The alpha value present in the font image is scaled by the alpha argument given to the routine + Alpha should be between 0 and 256 */ + void vc_image_font_alpha_blt(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int red, int green, int blue, int alpha); + + /* Utilities for image editor */ + extern void im_split_components(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, + int width, int height, int pitch); + + extern void im_merge_components(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, + int width, int height, int pitch); + + extern void im_merge_components_adjacent(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, + int width, int height, int pitch, + int badjacent); + + /* Deflicker a YUV image using a simple low pass filter */ + void vc_image_deflicker(VC_IMAGE_BUF_T *img, unsigned char *context); + + /* Blend an Interlaced YUV image using a simple low pass filter which works on colour as well as luma*/ + void vc_image_blend_interlaced(VC_IMAGE_BUF_T *img, unsigned char *context); + + /* Stripe resizing. Resize a 16-high stripe horizontally to have the given width, + or a 16-wide column to have the given height. Work in place, uses bilinear filtering. */ + void vc_image_resize_stripe_h(VC_IMAGE_BUF_T *image, int d_width, int s_width); + void vc_image_resize_stripe_v(VC_IMAGE_BUF_T *image, int d_height, int s_height); + + /* Stripe resizing. Resize a horizontal stripe to 16-high, or a vertical stripe to 16 wide. + Work in place, uses bilinear filtering. */ + void vc_image_decimate_stripe_h(VC_IMAGE_BUF_T *src, int offset, int step); + void vc_image_decimate_stripe_v(VC_IMAGE_BUF_T *src, int offset, int step); + + + extern int vc_image_set_alpha(VC_IMAGE_BUF_T *image, + const int x, + const int y, + const int width, + const int height, + const int alpha ); + + /* Make a horizontal alpha gradient mask for used in alpha blending below */ + extern void vc_image_make_alpha_gradient(VC_IMAGE_BUF_T *pimage, + int start_alpha, int end_alpha); + + /* Alpha blend two images together using an alpha mask */ + extern void vc_image_alphablend(VC_IMAGE_BUF_T *dest, VC_IMAGE_BUF_T *src, VC_IMAGE_BUF_T *mask, + int dest_xoffset, int dest_yoffset, + int src_xoffset, int src_yoffset, + int width, int height); + + typedef struct vc_image_pool_s * VC_IMAGE_POOL_HANDLE_T; + typedef VC_IMAGE_POOL_HANDLE_T VC_IMAGE_POOL_HANDLE; /* legacy */ + + /* Blt a region of an image onto a particular mipmap of a brcm1 image */ + /* XXX do not use this function - the interface is likely to change as I decide + * what options are available here */ + void vc_image_XXX_mipmap_blt_XXX(VC_IMAGE_BUF_T *dest, int x_offset, int y_offset, int width, int height, + VC_IMAGE_BUF_T *src, int src_x_offset, int src_y_offset, int miplvl, int cubeface, VC_IMAGE_TRANSFORM_T transform); + + /* Function to calculate the crc of the VC_IMAGE */ + VC_IMAGE_CRC_T vc_image_calc_crc_interlaced(VC_IMAGE_BUF_T *img, int cl, int cr, int ct, int cb, int field); + + /* make compatible with old function */ + #define vc_image_calc_crc(img,cl,cr,ct,cb) vc_image_calc_crc_interlaced((img),(cl),(cr),(ct),(cb),-1) + +#define vc_image_calc_crc_fast vc_image_calc_crc + +#if defined(NDEBUG) +# define vc_image_debugstr(x) NULL +#else +# define vc_image_debugstr(x) (x) +#endif + +#define vc_image_vparmalloc(type, desc, width, height, proplist) vc_image_vpmalloc(type, vc_image_debugstr(desc), width, height, proplist) +#if __STDC_VERSION__ >= 199901L || _MSC_VER >= 1400 +# define vc_image_parmalloc(type, desc, ...) vc_image_parmalloc_unwrapped(type, vc_image_debugstr(desc), __VA_ARGS__, VC_IMAGE_PROP_END) +# define vc_image_configure(...) vc_image_configure_unwrapped(__VA_ARGS__, VC_IMAGE_PROP_END) +# define vc_image_reconfigure(...) vc_image_reconfigure_unwrapped(__VA_ARGS__, VC_IMAGE_PROP_END) +#elif !defined(_MSC_VER) && !defined(VCMODS_LCC) +# define vc_image_parmalloc(type, desc, arglist...) vc_image_parmalloc_unwrapped(type, vc_image_debugstr(desc), arglist, VC_IMAGE_PROP_END) +# define vc_image_configure(image, arglist...) vc_image_configure_unwrapped(image, arglist, VC_IMAGE_PROP_END) +# define vc_image_reconfigure(image, arglist...) vc_image_reconfigure_unwrapped(image, arglist, VC_IMAGE_PROP_END) +#else + /*# warning "Your compiler does not support variadic macros. You'll have no vc_image_configure() or vc_image_parmalloc() functions."*/ +#endif + +// now included in vc_image.c as openmaxil uses it regardless of which formats are built for +extern void vc_subsample(VC_IMAGE_BUF_T *sub,VC_IMAGE_BUF_T *cur); + +#ifdef USE_YUV_UV + +extern void vc_save_YUV( VC_IMAGE_BUF_T *frame, + const char * const sz_filename); + +extern void vc_save_YUV_1file( VC_IMAGE_BUF_T *frame, const char * const sz_filename, int framenum ); + +extern void vc_load_YUVUV( VC_IMAGE_BUF_T *frame, + const char * const sz_filename,int yuvwidth,int yuvheight); + +extern void vc_load_YUV( VC_IMAGE_BUF_T *frame, + const char * const sz_filename,int framenum); + +extern void vc_endianreverse32(uint32_t *addr,int count); + +/* +** This is my preferred function to load a YUV_UV image. It works with either a collection of images +** e.g. foo%03u.yuv or one giant concatenated file e.g. foo.yuv. +** It will tile or truncate the output image to what you want which is why there are two heights & widths. +** Return 0 if successful; -1 if the params are unsuitable, -2 if we couldn't open the file. +*/ +extern int vc_load_image( const char * const sz_file_fmt, /* in */ + const unsigned iim, /* in */ + const int pitch, /* in */ + const int i_height, /* in */ /* (i)nput image: i.e. raw file */ + const int i_width, /* in */ + const int o_height, /* in */ /* (o)utput image: i.e. framebuffer */ + const int o_width, /* in */ + const VC_IMAGE_BUF_T * const frame /* in */ ); + +/* Modified version of vc_load_image (for maximum efficiency): + * - must be supplied with a buffer to read a whole YUV frame into + * - reads the next sequential image in an already-open file (no fopen/fseek/fclose). + * - no tiling: output size = input size. + */ +void vc_slurp_image (FILE * fid, /* in */ + const int pitch, /* in */ + const int height, /* in */ + const int width, /* in */ + const VC_IMAGE_T * const frame, /* in */ + unsigned char * frame_buf, /* in */ + int rewind); /* in */ + +extern int vc_load_image_vowifi( void * infile_fid, + const unsigned iim, /* in */ + const int pitch, /* in */ + const int i_height, /* in */ /* (i)nput image: i.e. raw file */ + const int i_width, /* in */ + const int o_height, /* in */ /* (o)utput image: i.e. framebuffer */ + const int o_width, /* in */ + const VC_IMAGE_BUF_T * const frame /* in */ ) ; + +extern int vc_load_image_vowifi_brcm1d( void * infile_fid, + const unsigned iim, /* in */ + const int pitch, /* in */ + const int i_height, /* in */ /* (i)nput image: i.e. raw file */ + const int i_width, /* in */ + const int o_height, /* in */ /* (o)utput image: i.e. framebuffer */ + const int o_width, /* in */ + const VC_IMAGE_BUF_T * const frame, /* in */ + const unsigned char *frame_buf) ; + +/* +** Vector routine to difference a pair of images ( d |-|= a ). +** Should probably dcache flush after this if using via uncached alias? +*/ +extern +void vc_image_difference( VC_IMAGE_BUF_T * const d, /* in/out */ + const VC_IMAGE_BUF_T * const a, /* in */ + float * const psnr_y, /* out */ + float * const psnr_uv, /* out */ + unsigned * const max_y, /* out */ + unsigned * const max_uv /* out */ ); + + +/* +** The h.263 Annex J deblocker. +** ("dest" & "src" may point to the same storage.) +*/ +extern +void vc_image_deblock_h263( VC_IMAGE_BUF_T * const dest, /*(in)out */ + const VC_IMAGE_BUF_T * const src, /* in */ + const int QUANT /* in */ ); + +/* + * Clone the top field of an interlaced image (lines 0,2,4,..) over the bottom field (lines 1,3,5,...) + * or vice versa +*/ +extern +void vc_image_clone_field(VC_IMAGE_T * image, int clone_top_field); + +#endif + + + /* RGBA32 colour macros */ +#define RGBA_ALPHA_TRANSPARENT 0 +#define RGBA_ALPHA_OPAQUE 255 + +#define RGBA32_TRANSPARENT_COLOUR 0x00000000 +#ifdef RGB888 +#define RGBA32_APIXEL(r, g, b, a) (((unsigned long)(a) << 24) | ((b) << 16) | ((g) << 8) | (r)) +#define RGBA32_SPIXEL(r, g, b, a) ((RGBA_ALPHA_OPAQUE << 24) | ((b) << 16) | ((g) << 8) | (r)) +#else +#define RGBA32_APIXEL(r, g, b, a) (((unsigned long)(a) << 24) | ((r) << 16) | ((g) << 8) | (b)) +#define RGBA32_SPIXEL(r, g, b, a) (((unsigned long)RGBA_ALPHA_OPAQUE << 24) | ((r) << 16) | ((g) << 8) | (b)) +#endif +#define RGBA32_PIXEL(r, g, b, a) ((a) >= 255 ? RGBA32_SPIXEL(r, g, b, a) : RGBA32_APIXEL(r, g, b, a)) +#define RGBA32_ALPHA(rgba) ((unsigned long)rgba >> 24) + + + /* RGBA16 colour macros */ +#define RGBA16_APIXEL(r, g, b, a) (((r) >> 4 << 12) | ((g) >> 4 << 8) | ((b) >> 4 << 4) | ((a) >> 4 << 0)) +#define RGBA16_SPIXEL(r, g, b) (((r) >> 4 << 12) | ((g) >> 4 << 8) | ((b) >> 4 << 4) | 0x000F) +#define RGBA16_PIXEL(r, g, b, a) RGBA16_APIXEL(r, g, b, a) + + /* RGBA565 colour macros */ +#define RGBA565_TRANSPARENTBITS 0x18DF +#define RGBA565_TRANSPARENTKEY 0xE700 +#define RGBA565_ALPHABITS 0x001C + +#define RGBA565_TRANSPARENT_COLOUR RGBA565_TRANSPARENTKEY + +#define RGBA565_APIXEL(r, g, b, a) (((r) >> 6 << 11) | ((g) >> 6 << 6) | (((a)+16) >> 5 << 2) | ((b) >> 6) | RGBA565_TRANSPARENTKEY) +#define RGBA565_SPIXEL(r, g, b) (((r) >> 3 << 11) | ((g) >> 2 << 5) | ((b) >> 3) | (((r) & (g) & 0xE0) == 0xE0 ? 0x20 : 0x00)) +#define RGBA565_PIXEL(r, g, b, a) ((a) >= 0xF0 ? RGBA565_SPIXEL(r, g, b) : RGBA565_APIXEL(r, g, b, a)) + +#define RGBA565_FROM_RGB565(rgb) ((((rgb) & ~RGBA565_TRANSPARENTBITS) == RGBA565_TRANSPARENTKEY) ? (rgb) ^ 0x0020 : (rgb)) +#define RGBA565_TO_RGB565(rgba) ((((rgba) & ~RGBA565_TRANSPARENTBITS) == RGBA565_TRANSPARENTKEY) ? ((rgba) & (RGBA565_TRANSPARENTBITS ^ RGBA565_ALPHABITS)) * 10 : (rgba)) +#define RGBA565_ALPHA(rgba) ((((rgba) & ~RGBA565_TRANSPARENTBITS) == RGBA565_TRANSPARENTKEY) ? ((rgba) & RGBA565_ALPHABITS) << 3 : 0x100) + +#endif //#if !defined __SYMBIAN32__ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/helpers/vc_image/vc_image_helper.h b/helpers/vc_image/vc_image_helper.h new file mode 100755 index 0000000..f11d945 --- /dev/null +++ b/helpers/vc_image/vc_image_helper.h @@ -0,0 +1,252 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_IMAGE_HELPER_H +#define VC_IMAGE_HELPER_H + +#include "interface/vctypes/vc_image_structs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Image buffer object, with image data locked in memory and ready for access. + * + * This data type is fully compatible with \c VC_IMAGE_T for backwards + * compatibility. New code should use this type where the object refers to + * a locked image. + */ +typedef VC_IMAGE_T VC_IMAGE_BUF_T; + +/* Macros to determine which format a vc_image is */ + +typedef struct +{ + unsigned bits_per_pixel : 8, + is_rgb : 1, + is_yuv : 1, + is_raster_order : 1, + is_tformat_order : 1, + has_alpha : 1; +} VC_IMAGE_TYPE_INFO_T; + +#define VC_IMAGE_COMPONENT_ORDER(red_lsb, green_lsb, blue_lsb, alpha_lsb) \ + ( (((red_lsb) & 0x1f) << 0) \ + | (((green_lsb) & 0x1f) << 6) \ + | (((blue_lsb) & 0x1f) << 12) \ + | (((alpha_lsb) & 0x1f) << 18) ) +#define VC_IMAGE_RED_OFFSET(component_order) (((component_order) >> 0) & 0x1f) +#define VC_IMAGE_GREEN_OFFSET(component_order) (((component_order) >> 6) & 0x1f) +#define VC_IMAGE_BLUE_OFFSET(component_order) (((component_order) >> 12) & 0x1f) +#define VC_IMAGE_ALPHA_OFFSET(component_order) (((component_order) >> 18) & 0x1f) + +extern const VC_IMAGE_TYPE_INFO_T vc_image_type_info[VC_IMAGE_MAX + 1]; +extern const unsigned int vc_image_rgb_component_order[VC_IMAGE_MAX + 1]; + +#define VC_IMAGE_IS_YUV(type) (vc_image_type_info[type].is_yuv) +#define VC_IMAGE_IS_RGB(type) (vc_image_type_info[type].is_rgb) +#define VC_IMAGE_IS_RASTER(type) (vc_image_type_info[type].is_raster_order) +#define VC_IMAGE_IS_TFORMAT(type) (vc_image_type_info[type].is_tformat_order) +#define VC_IMAGE_BITS_PER_PIXEL(type) (vc_image_type_info[type].bits_per_pixel) +#define VC_IMAGE_HAS_ALPHA(type) (vc_image_type_info[type].has_alpha) + +#define case_VC_IMAGE_ANY_YUV \ + case VC_IMAGE_YUV420: \ + case VC_IMAGE_YUV420SP: \ + case VC_IMAGE_YUV422: \ + case VC_IMAGE_YUV_UV: \ + case VC_IMAGE_YUV_UV32: \ + case VC_IMAGE_YUV422PLANAR: \ + case VC_IMAGE_YUV444PLANAR: \ + case VC_IMAGE_YUV420_16: \ + case VC_IMAGE_YUV_UV_16 + +#define case_VC_IMAGE_ANY_RGB \ + case VC_IMAGE_RGB565: \ + case VC_IMAGE_RGB2X9: \ + case VC_IMAGE_RGB666: \ + case VC_IMAGE_RGBA32: \ + case VC_IMAGE_RGBX32: \ + case VC_IMAGE_RGBA16: \ + case VC_IMAGE_RGBA565: \ + case VC_IMAGE_RGB888: \ + case VC_IMAGE_TF_RGBA32: \ + case VC_IMAGE_TF_RGBX32: \ + case VC_IMAGE_TF_RGBA16: \ + case VC_IMAGE_TF_RGBA5551: \ + case VC_IMAGE_TF_RGB565: \ + case VC_IMAGE_BGR888: \ + case VC_IMAGE_BGR888_NP: \ + case VC_IMAGE_ARGB8888: \ + case VC_IMAGE_XRGB8888 + +#define case_VC_IMAGE_ANY_RGB_NOT_TF \ + case VC_IMAGE_RGB565: \ + case VC_IMAGE_RGB2X9: \ + case VC_IMAGE_RGB666: \ + case VC_IMAGE_RGBA32: \ + case VC_IMAGE_RGBX32: \ + case VC_IMAGE_RGBA16: \ + case VC_IMAGE_RGBA565: \ + case VC_IMAGE_RGB888: \ + case VC_IMAGE_BGR888: \ + case VC_IMAGE_BGR888_NP: \ + case VC_IMAGE_ARGB8888: \ + case VC_IMAGE_XRGB8888: \ + case VC_IMAGE_RGBX8888: \ + case VC_IMAGE_BGRX8888 + +#define case_VC_IMAGE_ANY_TFORMAT \ + case VC_IMAGE_TF_RGBA32: \ + case VC_IMAGE_TF_RGBX32: \ + case VC_IMAGE_TF_FLOAT: \ + case VC_IMAGE_TF_RGBA16: \ + case VC_IMAGE_TF_RGBA5551: \ + case VC_IMAGE_TF_RGB565: \ + case VC_IMAGE_TF_YA88: \ + case VC_IMAGE_TF_BYTE: \ + case VC_IMAGE_TF_PAL8: \ + case VC_IMAGE_TF_PAL4: \ + case VC_IMAGE_TF_ETC1: \ + case VC_IMAGE_TF_Y8: \ + case VC_IMAGE_TF_A8: \ + case VC_IMAGE_TF_SHORT: \ + case VC_IMAGE_TF_1BPP + +/****************************************************************************** +General functions. +******************************************************************************/ + +int vc_image_bits_per_pixel(VC_IMAGE_TYPE_T type); + +int calculate_pitch(VC_IMAGE_TYPE_T type, int width, int height, uint8_t num_channels, VC_IMAGE_INFO_T *info, VC_IMAGE_EXTRA_T *extra); + +/* Check if an image will use an alternate memory layout, in order to cope with + * codec limitation. Applies to YUV_UV images taller than 1344 lines. */ +int vc_image_is_tall_yuv_uv(VC_IMAGE_TYPE_T type, int height); + +/****************************************************************************** +Data member access. +******************************************************************************/ + +/* Set the type of the VC_IMAGE_T. */ +void vc_image_set_type(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type); + +/* Set the image_data field, noting how big it is. */ +void vc_image_set_image_data(VC_IMAGE_BUF_T *image, int size, void *image_data); + +/* Set the image data with added u and v pointers */ +void vc_image_set_image_data_yuv(VC_IMAGE_BUF_T *image, int size, void *image_y, void *image_u, void *image_v); + +/* Set the dimensions of the image. */ +void vc_image_set_dimensions(VC_IMAGE_T *image, int width, int height); + +/* Check the integrity of a VC_IMAGE_T structure - checks structure values + data ptr */ +int vc_image_verify(const VC_IMAGE_T *image); + +/* Set the pitch (internal_width) of the image. */ +void vc_image_set_pitch(VC_IMAGE_T *image, int pitch); + +/* Specify the vertical pitch for YUV planar images */ +void vc_image_set_vpitch(VC_IMAGE_T *image, int vpitch); + +/* Specify that the YUV image is V/U interleaved. */ +void vc_image_set_is_vu(VC_IMAGE_T *image); + +/* Return 1 if the YUV image is V/U interleaved, else return 0. */ +int vc_image_get_is_vu(const VC_IMAGE_T *image); +int vc_image_info_get_is_vu(const VC_IMAGE_INFO_T *info); + +/* Reset the shape of an image */ +int vc_image_reshape(VC_IMAGE_T *image, VC_IMAGE_TYPE_T type, int width, int height); + +/* Return the space required (in bytes) for an image of this type and dimensions. */ +int vc_image_required_size(VC_IMAGE_T *image); + +/* Return the space required (in bytes) for an image of this type's palette. */ +int vc_image_palette_size (VC_IMAGE_T *image); + +/* Return 1 if image is high-definition, else return 0. */ +int vc_image_is_high_definition(const VC_IMAGE_T *image); + +/* Return true if palette is 32bpp */ +int vc_image_palette_is_32bit(VC_IMAGE_T *image); + +/* Retrieve Y, U and V pointers from a YUV image. Note that these are macros. */ + +#define vc_image_get_y(p) ((unsigned char *)((p)->image_data)) + +// replaced with functions to allow assert - revert to #define when fixed +//#define vc_image_get_u(p) ((unsigned char *)((p)->extra.uv.u)) +//#define vc_image_get_v(p) ((unsigned char *)((p)->extra.uv.v)) +unsigned char *vc_image_get_u(const VC_IMAGE_BUF_T *image); +unsigned char *vc_image_get_v(const VC_IMAGE_BUF_T *image); + +/* Calculate the address of the given pixel coordinate -- the result may point + * to a word containing data for several pixels (ie., for sub-8bpp and + * compressed formats). + */ +void *vc_image_pixel_addr(VC_IMAGE_BUF_T *image, int x, int y); +void *vc_image_pixel_addr_mm(VC_IMAGE_BUF_T *image, int x, int y, int miplevel); +void *vc_image_pixel_addr_u(VC_IMAGE_BUF_T *image, int x, int y); +void *vc_image_pixel_addr_v(VC_IMAGE_BUF_T *image, int x, int y); + +/* As above, but with (0,0) in the bottom-left corner */ +void *vc_image_pixel_addr_gl(VC_IMAGE_BUF_T *image, int x, int y, int miplevel); + +#define vc_image_get_y_422(p) vc_image_get_y(p) +#define vc_image_get_u_422(p) vc_image_get_u(p) +#define vc_image_get_v_422(p) vc_image_get_v(p) + +#define vc_image_get_y_422planar(p) vc_image_get_y(p) +#define vc_image_get_u_422planar(p) vc_image_get_u(p) +#define vc_image_get_v_422planar(p) vc_image_get_v(p) + +/* Mipmap-related functions. Image must be t-format. */ + +/* Return the pitch of the selected mipmap */ +unsigned int vc_image_get_mipmap_pitch(VC_IMAGE_T *image, int miplvl); + +/* Return the padded height of the selected mipmap (mipmaps must be padded to a + * power of 2) */ +unsigned int vc_image_get_mipmap_padded_height(VC_IMAGE_T *image, int miplvl); + +/* Return the offset, in bytes, of the selected mipmap. */ +int vc_image_get_mipmap_offset(VC_IMAGE_T *image, int miplvl); + +/* Return whether the selected mipmap is stored in t-format or linear microtile + * format. */ +#define VC_IMAGE_MIPMAP_TFORMAT 0 +#define VC_IMAGE_MIPMAP_LINEAR_TILE 1 +int vc_image_get_mipmap_type(VC_IMAGE_T const *image, int miplvl); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/helpers/vc_image/vc_image_metadata.h b/helpers/vc_image/vc_image_metadata.h new file mode 100755 index 0000000..9521e49 --- /dev/null +++ b/helpers/vc_image/vc_image_metadata.h @@ -0,0 +1,99 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VC_IMAGE_METADATA_H +#define VC_IMAGE_METADATA_H + +#include "vcfw/rtos/rtos.h" +#include "helpers/vc_image/metadata_fourcc.h" + +#define VC_METADATA_ALIGN 3 + +/****************************************************************************** +Types of metadata. +******************************************************************************/ +typedef unsigned int VC_METADATA_TYPE_T; + +/****************************************************************************** +Metadata and item headers. +******************************************************************************/ +typedef struct vc_metadata_item_s { + VC_METADATA_TYPE_T type; + int len; +} VC_METADATA_ITEM_T; + + +typedef struct vc_metadata_header_s { + int size; +#ifdef VCMODS_LCC + unsigned char readonly; +#else + unsigned char readonly:1; +#endif + int offset_next; + RTOS_LATCH_T latch; +} VC_METADATA_HEADER_T; + + +/****************************************************************************** +Public declarations. +******************************************************************************/ +// Initialises a metadata header structure +int vc_metadata_initialise(VC_METADATA_HEADER_T *header, int size); +// Add a metadata entry to the buffer +int vc_metadata_add(VC_METADATA_HEADER_T *header, void *data, VC_METADATA_TYPE_T type, int len); +// Get a (pointer to a) particular metadata item from the buffer (optionally copy the data to dest), and return the length of the item +void *vc_metadata_get_with_length(void *dest, VC_METADATA_HEADER_T *header, VC_METADATA_TYPE_T type, int *retcode, int *len); +// Get a (pointer to a) particular metadata item from the buffer (optionally copy the data to dest) +void *vc_metadata_get(void *dest, VC_METADATA_HEADER_T *header, VC_METADATA_TYPE_T type, int *retcode); +// Clear the metadata buffer and reset the header (clears readonly flag) +int vc_metadata_clear(VC_METADATA_HEADER_T *header); +// Set or clear the read-only flag in the metadata buffer +int vc_metadata_set_readonly(VC_METADATA_HEADER_T *header, int value); +// Copies the contents of one metadata buffer to the other (appends the destination buffer) +int vc_metadata_copy(VC_METADATA_HEADER_T *dest, VC_METADATA_HEADER_T *src); +// Overwrites an item in the metadata buffer (caller must make sure the sizes match!) +int vc_metadata_overwrite(VC_METADATA_HEADER_T *header, void *data, VC_METADATA_TYPE_T type); +// Flush the metadata buffer out of the cache +int vc_metadata_cache_flush(VC_METADATA_HEADER_T *header); +// Locks the vc_metadata library semaphore +int vc_metadata_lock(VC_METADATA_HEADER_T *header); +// Unlocks the vc_metadata library semaphore +int vc_metadata_unlock(VC_METADATA_HEADER_T *header); + +/****************************************************************************** +Wrappers for the above functions using VC_IMAGEs. +******************************************************************************/ +#define vc_image_metadata_initialise(i, s) vc_metadata_initialise((i)->metadata, s) +#define vc_image_metadata_add(i, d, t, l) vc_metadata_add((i)->metadata, d, t, l) +#define vc_image_metadata_get(d, i, t, r) vc_metadata_get(d, (i)->metadata, t, r) +#define vc_image_metadata_clear(i) vc_metadata_clear((i)->metadata) +#define vc_image_metadata_set_readonly(i, v) vc_metadata_set_readonly((i)->metadata, v) +#define vc_image_metadata_copy(d, s) vc_metadata_copy((d)->metadata, (s)->metadata) +#define vc_image_metadata_overwrite(i, d, t) vc_metadata_overwrite((i)->metadata, d, t) +#define vc_image_metadata_cache_flush(i) vc_metadata_cache_flush((i)->metadata) +#endif diff --git a/host_applications/android/apps/vidtex/CMakeLists.txt b/host_applications/android/apps/vidtex/CMakeLists.txt new file mode 100755 index 0000000..1f705ef --- /dev/null +++ b/host_applications/android/apps/vidtex/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.8) + +SET(COMPILE_DEFINITIONS -Werror -Wall) +include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/libs/bcm_host/include) + +set (VIDTEX_SOURCES + main.cpp + launcher_rpi.c + svp.c + vidtex.c) +add_executable(vidtex ${VIDTEX_SOURCES}) +target_link_libraries(vidtex GLESv2 EGL m bcm_host mmal_core mmal_components mmal_util mmal_vc_client vcos) diff --git a/host_applications/android/apps/vidtex/applog.h b/host_applications/android/apps/vidtex/applog.h new file mode 100755 index 0000000..94cce06 --- /dev/null +++ b/host_applications/android/apps/vidtex/applog.h @@ -0,0 +1,48 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef APPLOG_H +#define APPLOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file Application support for VCOS logging. + * This header file is used to abstract the logging for files that are designed to be included + * in different applications. It should be included before any other VMCS header files. + */ + +#define VCOS_LOG_CATEGORY (&app_log_category) +#include "interface/vcos/vcos.h" +extern VCOS_LOG_CAT_T app_log_category; + +#ifdef __cplusplus +} +#endif + +#endif /* APPLOG_H */ diff --git a/host_applications/android/apps/vidtex/launcher.h b/host_applications/android/apps/vidtex/launcher.h new file mode 100755 index 0000000..7169fa6 --- /dev/null +++ b/host_applications/android/apps/vidtex/launcher.h @@ -0,0 +1,87 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef LAUNCHER_H +#define LAUNCHER_H + +#include +#include +#include +#include +#include + +/** + * Android-specific launcher for native graphical application. + * See runApp(). + */ +class Launcher : public android::Thread +{ +public: + /** Entry point function for application-specific code. + * @param params Application-specific parameters. + * @param win Native window/surface. + * @return 0 on success; -1 on failure. + */ + typedef int (*RUN_APP_FN_T)(const void *params, EGLNativeWindowType win); + + /** Run a function in a separate thread. + * A separate thread is launched to run the function, and a native surface created for + * that application. + * @param name Application name. + * @param run_app_fn Entry point function for application. + * @param params Application-specific parameters. This will be bitwise copyable, and + * between C and C++, so don't use pointers, bool or anything else liable + * for problems. + * @param param_size Size, in bytes, of the application parameters. + * @return 0 on success; -1 on failure. + */ + static int runApp(const char *name, RUN_APP_FN_T run_app_fn, const void *params, + size_t param_size); + + Launcher(const char *name, RUN_APP_FN_T run_fn, const void *params, size_t param_size); + virtual ~Launcher(); + + android::sp session() const; + +protected: + virtual android::status_t readyToRun(); + virtual bool threadLoop(); + +private: + virtual void onFirstRef(); + + android::sp m_session; + android::sp m_sf_control; + android::sp m_sf_surface; + + android::String8 m_name; + RUN_APP_FN_T m_run_fn; + android::Vector m_params; + int m_result; +}; + +#endif diff --git a/host_applications/android/apps/vidtex/launcher_rpi.c b/host_applications/android/apps/vidtex/launcher_rpi.c new file mode 100755 index 0000000..f349836 --- /dev/null +++ b/host_applications/android/apps/vidtex/launcher_rpi.c @@ -0,0 +1,87 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "launcher_rpi.h" + +#include +#include +#include +#include "bcm_host.h" + +static int create_native_window(EGL_DISPMANX_WINDOW_T *native_win) +{ + int32_t status = 0; + DISPMANX_DISPLAY_HANDLE_T disp; + DISPMANX_ELEMENT_HANDLE_T elem; + DISPMANX_UPDATE_HANDLE_T update; + VC_DISPMANX_ALPHA_T alpha = {DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0}; + VC_RECT_T src_rect = {0}; + VC_RECT_T dest_rect = {0}; + uint32_t display_width, display_height; + uint32_t disp_num = 0; // Primary + uint32_t layer_num = 0; + + status = graphics_get_display_size(0, &display_width, &display_height); + if (status != 0) + return status; + + /* Fullscreen */ + display_width = 1280; + display_height = 720; + dest_rect.width = display_width; + dest_rect.height = display_height; + src_rect.width = display_width << 16; + src_rect.height = display_height << 16; + + disp = vc_dispmanx_display_open(disp_num); + update = vc_dispmanx_update_start(0); + elem = vc_dispmanx_element_add(update, disp, layer_num, &dest_rect, 0, + &src_rect, DISPMANX_PROTECTION_NONE, &alpha, NULL, DISPMANX_NO_ROTATE); + + native_win->element = elem; + native_win->width = display_width; + native_win->height = display_height; + vc_dispmanx_update_submit_sync(update); + + return 0; +} + +int runApp(const char *name, RUN_APP_FN_T run_app_fn, const void *params, size_t param_size) +{ + EGL_DISPMANX_WINDOW_T win; + (void) param_size; + + + vcos_log_trace("Initialsing BCM HOST"); + bcm_host_init(); + + vcos_log_trace("Starting '%s'", name); + if (create_native_window(&win) != 0) + return -1; + + return run_app_fn(params, (EGLNativeWindowType *) &win); +} + diff --git a/host_applications/android/apps/vidtex/launcher_rpi.h b/host_applications/android/apps/vidtex/launcher_rpi.h new file mode 100755 index 0000000..335fdc4 --- /dev/null +++ b/host_applications/android/apps/vidtex/launcher_rpi.h @@ -0,0 +1,61 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef LAUNCHER_RPI_H +#define LAUNCHER_RPI_H + +#include +#include +#include "applog.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Entry point function for application-specific code. + * @param params Application-specific parameters. + * @param win Native window/surface. + * @return 0 on success; -1 on failure. + */ +typedef int (*RUN_APP_FN_T)(const void *params, EGLNativeWindowType win); + +/** Create native window and runs the function. + * + * No need to run in a separate thread on Linux / RPI. + * + * @param name Application name. + * @param run_app_fn Entry point function for application. + * @param params Application-specific parameters. + * @param param_size Size, in bytes, of the application parameters. + * @return 0 on success; -1 on failure. + */ +int runApp(const char *name, RUN_APP_FN_T run_app_fn, const void *params, size_t param_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/host_applications/android/apps/vidtex/main.cpp b/host_applications/android/apps/vidtex/main.cpp new file mode 100755 index 0000000..701574d --- /dev/null +++ b/host_applications/android/apps/vidtex/main.cpp @@ -0,0 +1,115 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include "applog.h" +#include "vidtex.h" + +#ifdef __ANDROID__ +#include "launcher.h" +#else +#include "launcher_rpi.h" +#endif + +VCOS_LOG_CAT_T app_log_category; + +static int launch_vidtex(const void *params, EGLNativeWindowType win) +{ + return vidtex_run((const VIDTEX_PARAMS_T *)params, win); +} + +static void usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s [-d duration-ms] [-i] [uri]\n", argv0); +} + +int main(int argc, char **argv) +{ + // Parse command line options/arguments. + VIDTEX_PARAMS_T params = {0}; + int rc; + int status = 0; + + opterr = 0; + + while ((rc = getopt(argc, argv, "d:iuvy")) != -1) + { + switch (rc) + { + case 'd': + params.duration_ms = atoi(optarg); + break; + + case 'i': + params.opts |= VIDTEX_OPT_IMG_PER_FRAME; + break; + + case 'y': + params.opts |= VIDTEX_OPT_Y_TEXTURE; + break; + + case 'u': + params.opts |= VIDTEX_OPT_U_TEXTURE; + break; + + case 'v': + params.opts |= VIDTEX_OPT_V_TEXTURE; + break; + + default: + usage(argv[0]); + return 2; + } + } + + if (optind < argc - 1) + { + usage(argv[0]); + return 2; + } + + if (optind < argc) + { + strncpy(params.uri, argv[optind], sizeof(params.uri) - 1); + params.uri[sizeof(params.uri) - 1] = '\0'; + } + + // Init vcos logging. + vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_INFO); + vcos_log_register("vidtex", VCOS_LOG_CATEGORY); + + // Run video-on-texture application in a separate thread. +#ifdef __ANDROID__ + status = Launcher::runApp("vidtex", launch_vidtex, ¶ms, sizeof(params)); +#else + status = runApp("vidtex", launch_vidtex, ¶ms, sizeof(params)); +#endif + return (status == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/host_applications/android/apps/vidtex/svp.c b/host_applications/android/apps/vidtex/svp.c new file mode 100755 index 0000000..89b576a --- /dev/null +++ b/host_applications/android/apps/vidtex/svp.c @@ -0,0 +1,637 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include "applog.h" +#include "interface/vcos/vcos_stdbool.h" +#include "interface/vcos/vcos_inttypes.h" +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/util/mmal_connection.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "svp.h" + +#define CHECK_STATUS(s, m) \ + if ((s) != MMAL_SUCCESS) { \ + LOG_ERROR("%s: %s", (m), mmal_status_to_string((s))); \ + goto error; \ + } + +/* Flags specifying fields of SVP_T struct */ +#define SVP_CREATED_SEM (1 << 0) +#define SVP_CREATED_THREAD (1 << 1) +#define SVP_CREATED_MUTEX (1 << 2) +#define SVP_CREATED_TIMER (1 << 3) +#define SVP_CREATED_WD_TIMER (1 << 4) + +/* Hard-coded camera parameters */ +#if 0 +#define SVP_CAMERA_WIDTH 1920 +#define SVP_CAMERA_HEIGHT 1080 +#else +#define SVP_CAMERA_WIDTH 1280 +#define SVP_CAMERA_HEIGHT 720 +#endif +#define SVP_CAMERA_FRAMERATE 30 +#define SVP_CAMERA_DURATION_MS 10000 + +/* Watchdog timeout - elapsed time to allow for no video frames received */ +#define SVP_WATCHDOG_TIMEOUT_MS 5000 + +/** Simple video player instance */ +struct SVP_T +{ + /* Player options */ + SVP_OPTS_T opts; + + /* Bitmask of SVP_CREATED_XXX values indicating which fields have been created. + * Only used for those fields which can't be (portably) determined from their + * own value. + */ + uint32_t created; + + /* Semaphore used for synchronising buffer handling for decoded frames */ + VCOS_SEMAPHORE_T sema; + + /* User supplied callbacks */ + SVP_CALLBACKS_T callbacks; + + /* Container reader component */ + MMAL_COMPONENT_T *reader; + + /* Video decoder component */ + MMAL_COMPONENT_T *video_decode; + + /* Camera component */ + MMAL_COMPONENT_T *camera; + + /* Connection: container reader -> video decoder */ + MMAL_CONNECTION_T *connection; + + /* Output port from video decoder or camera */ + MMAL_PORT_T *video_output; + + /* Pool of buffers for video decoder output */ + MMAL_POOL_T *out_pool; + + /* Queue to hold decoded video frames */ + MMAL_QUEUE_T *queue; + + /* Worker thread */ + VCOS_THREAD_T thread; + + /* Timer to trigger stop in camera preview case */ + VCOS_TIMER_T timer; + + /* Watchdog timer */ + VCOS_TIMER_T wd_timer; + + /* Mutex to synchronise access to all following fields */ + VCOS_MUTEX_T mutex; + + /* Stop control: 0 to process stream; bitmask of SVP_STOP_XXX values to stop */ + uint32_t stop; + + /* Player stats */ + SVP_STATS_T stats; +}; + +/* Local function prototypes */ +static MMAL_STATUS_T svp_setup_reader(MMAL_COMPONENT_T *reader, const char *uri, + MMAL_PORT_T **video_port); +static void svp_timer_cb(void *ctx); +static void svp_watchdog_cb(void *ctx); +static MMAL_STATUS_T svp_port_enable(SVP_T *svp, MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb); +static void *svp_worker(void *arg); +static void svp_bh_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); +static void svp_bh_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); +static void svp_reset_stop(SVP_T *svp); +static void svp_set_stop(SVP_T *svp, uint32_t stop_flags); +static uint32_t svp_get_stop(SVP_T *svp); + +/* Create Simple Video Player instance. */ +SVP_T *svp_create(const char *uri, SVP_CALLBACKS_T *callbacks, const SVP_OPTS_T *opts) +{ + SVP_T *svp; + MMAL_STATUS_T st; + VCOS_STATUS_T vst; + MMAL_PORT_T *reader_output = NULL; + MMAL_COMPONENT_T *video_decode = NULL; + MMAL_PORT_T *video_output = NULL; + + LOG_TRACE("Creating player for %s", (uri ? uri : "camera preview")); + + vcos_assert(callbacks->video_frame_cb); + vcos_assert(callbacks->stop_cb); + + svp = vcos_calloc(1, sizeof(*svp), "svp"); + CHECK_STATUS((svp ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to allocate context"); + + svp->opts = *opts; + svp->callbacks = *callbacks; + + /* Semaphore used for synchronising buffer handling for decoded frames */ + vst = vcos_semaphore_create(&svp->sema, "svp-sem", 0); + CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create semaphore"); + svp->created |= SVP_CREATED_SEM; + + vst = vcos_mutex_create(&svp->mutex, "svp-mutex"); + CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create mutex"); + svp->created |= SVP_CREATED_MUTEX; + + vst = vcos_timer_create(&svp->timer, "svp-timer", svp_timer_cb, svp); + CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create timer"); + svp->created |= SVP_CREATED_TIMER; + + vst = vcos_timer_create(&svp->wd_timer, "svp-wd-timer", svp_watchdog_cb, svp); + CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create timer"); + svp->created |= SVP_CREATED_WD_TIMER; + + /* Create components */ + svp->reader = NULL; + svp->video_decode = NULL; + svp->camera = NULL; + svp->connection = NULL; + + if (uri) + { + /* Video from URI: setup container_reader -> video_decode */ + + /* Create and set up container reader */ + st = mmal_component_create(MMAL_COMPONENT_DEFAULT_CONTAINER_READER, &svp->reader); + CHECK_STATUS(st, "Failed to create container reader"); + + st = svp_setup_reader(svp->reader, uri, &reader_output); + if (st != MMAL_SUCCESS) + goto error; + + st = mmal_component_enable(svp->reader); + CHECK_STATUS(st, "Failed to enable container reader"); + + st = svp_port_enable(svp, svp->reader->control, svp_bh_control_cb); + CHECK_STATUS(st, "Failed to enable container reader control port"); + + /* Create and set up video decoder */ + st = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &svp->video_decode); + CHECK_STATUS(st, "Failed to create video decoder"); + + video_decode = svp->video_decode; + video_output = video_decode->output[0]; + + st = mmal_component_enable(video_decode); + CHECK_STATUS(st, "Failed to enable video decoder"); + + st = svp_port_enable(svp, video_decode->control, svp_bh_control_cb); + CHECK_STATUS(st, "Failed to enable video decoder control port"); + } + else + { + /* Camera preview */ + st = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &svp->camera); + CHECK_STATUS(st, "Failed to create camera"); + + st = mmal_component_enable(svp->camera); + CHECK_STATUS(st, "Failed to enable camera"); + + st = svp_port_enable(svp, svp->camera->control, svp_bh_control_cb); + CHECK_STATUS(st, "Failed to enable camera control port"); + + video_output = svp->camera->output[0]; /* Preview port */ + } + + st = mmal_port_parameter_set_boolean(video_output, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); + CHECK_STATUS((st == MMAL_ENOSYS ? MMAL_SUCCESS : st), "Failed to enable zero copy"); + + if (uri) + { + /* Create connection: container_reader -> video_decoder */ + st = mmal_connection_create(&svp->connection, reader_output, video_decode->input[0], + MMAL_CONNECTION_FLAG_TUNNELLING); + CHECK_STATUS(st, "Failed to create connection"); + } + + /* Set video output port format. + * Opaque encoding ensures we get buffer data as handles to relocatable heap. */ + video_output->format->encoding = MMAL_ENCODING_OPAQUE; + + if (!uri) + { + /* Set video format for camera preview */ + MMAL_VIDEO_FORMAT_T *vfmt = &video_output->format->es->video; + + CHECK_STATUS((video_output->format->type == MMAL_ES_TYPE_VIDEO) ? MMAL_SUCCESS : MMAL_EINVAL, + "Output port isn't video format"); + + vfmt->width = SVP_CAMERA_WIDTH; + vfmt->height = SVP_CAMERA_HEIGHT; + vfmt->crop.x = 0; + vfmt->crop.y = 0; + vfmt->crop.width = vfmt->width; + vfmt->crop.height = vfmt->height; + vfmt->frame_rate.num = SVP_CAMERA_FRAMERATE; + vfmt->frame_rate.den = 1; + } + + st = mmal_port_format_commit(video_output); + CHECK_STATUS(st, "Failed to set output port format"); + + /* Finally, set buffer num/size. N.B. For container_reader/video_decode, must be after + * connection created, in order for port format to propagate. + * Don't enable video output port until want to produce frames. */ + video_output->buffer_num = video_output->buffer_num_recommended; + video_output->buffer_size = video_output->buffer_size_recommended; + + /* Pool + queue to hold decoded video frames */ + svp->out_pool = mmal_port_pool_create(video_output, video_output->buffer_num, + video_output->buffer_size); + CHECK_STATUS((svp->out_pool ? MMAL_SUCCESS : MMAL_ENOMEM), "Error allocating pool"); + svp->queue = mmal_queue_create(); + CHECK_STATUS((svp ? MMAL_SUCCESS : MMAL_ENOMEM), "Error allocating queue"); + + svp->video_output = video_output; + + return svp; + +error: + svp_destroy(svp); + return NULL; +} + +/** + * Setup container reader component. + * Sets URI, to initialize processing, and finds a video track. + * @param reader Container reader component. + * @param uri Media URI. + * @param video_port On success, the output port for the first video track is returned here. + * @return MMAL_SUCCESS if the container reader was successfully set up and a video track located. + */ +static MMAL_STATUS_T svp_setup_reader(MMAL_COMPONENT_T *reader, const char *uri, + MMAL_PORT_T **video_port) +{ + MMAL_STATUS_T st; + uint32_t track; + + st = mmal_util_port_set_uri(reader->control, uri); + if (st != MMAL_SUCCESS) + { + LOG_ERROR("%s: couldn't open uri %s", reader->name, uri); + return st; + } + + /* Find a video track */ + for (track = 0; track < reader->output_num; track++) + { + if (reader->output[track]->format->type == MMAL_ES_TYPE_VIDEO) + { + break; + } + } + + if (track == reader->output_num) + { + LOG_ERROR("%s: no video track", uri); + return MMAL_EINVAL; + } + + *video_port = reader->output[track]; + return MMAL_SUCCESS; +} + +/* Destroy SVP instance. svp may be NULL. */ +void svp_destroy(SVP_T *svp) +{ + if (svp) + { + MMAL_COMPONENT_T *components[] = { svp->reader, svp->video_decode, svp->camera }; + MMAL_COMPONENT_T **comp; + + /* Stop thread, disable connection and components */ + svp_stop(svp); + + for (comp = components; comp < components + vcos_countof(components); comp++) + { + mmal_component_disable(*comp); + } + + /* Destroy connection + components */ + if (svp->connection) + { + mmal_connection_destroy(svp->connection); + } + + for (comp = components; comp < components + vcos_countof(components); comp++) + { + mmal_component_destroy(*comp); + } + + /* Free remaining resources */ + if (svp->out_pool) + { + mmal_pool_destroy(svp->out_pool); + } + + if (svp->queue) + { + mmal_queue_destroy(svp->queue); + } + + if (svp->created & SVP_CREATED_WD_TIMER) + { + vcos_timer_delete(&svp->wd_timer); + } + + if (svp->created & SVP_CREATED_TIMER) + { + vcos_timer_delete(&svp->timer); + } + + if (svp->created & SVP_CREATED_MUTEX) + { + vcos_mutex_delete(&svp->mutex); + } + + if (svp->created & SVP_CREATED_SEM) + { + vcos_semaphore_delete(&svp->sema); + } + + vcos_free(svp); + } +} + +/* Start SVP. Enables MMAL connection + creates worker thread. */ +int svp_start(SVP_T *svp) +{ + MMAL_STATUS_T st; + VCOS_STATUS_T vst; + + /* Ensure SVP is stopped first */ + svp_stop(svp); + + /* Reset the worker thread stop status, before enabling ports that might trigger a stop */ + svp_reset_stop(svp); + + if (svp->connection) + { + /* Enable reader->decoder connection */ + st = mmal_connection_enable(svp->connection); + CHECK_STATUS(st, "Failed to create connection"); + } + + /* Enable video output port */ + st = svp_port_enable(svp, svp->video_output, svp_bh_output_cb); + CHECK_STATUS(st, "Failed to enable output port"); + + /* Reset stats */ + svp->stats.video_frame_count = 0; + + /* Create worker thread */ + vst = vcos_thread_create(&svp->thread, "svp-worker", NULL, svp_worker, svp); + CHECK_STATUS((vst == VCOS_SUCCESS ? MMAL_SUCCESS : MMAL_ENOMEM), "Failed to create connection"); + + svp->created |= SVP_CREATED_THREAD; + + /* Set timer */ + if (svp->camera) + { + unsigned ms = svp->opts.duration_ms; + vcos_timer_set(&svp->timer, ((ms == 0) ? SVP_CAMERA_DURATION_MS : ms)); + } + + /* Start watchdog timer */ + vcos_timer_set(&svp->wd_timer, SVP_WATCHDOG_TIMEOUT_MS); + + return 0; + +error: + return -1; +} + +/* Stop SVP. Stops worker thread + disables MMAL connection. */ +void svp_stop(SVP_T *svp) +{ + vcos_timer_cancel(&svp->wd_timer); + vcos_timer_cancel(&svp->timer); + + /* Stop worker thread */ + if (svp->created & SVP_CREATED_THREAD) + { + svp_set_stop(svp, SVP_STOP_USER); + vcos_semaphore_post(&svp->sema); + vcos_thread_join(&svp->thread, NULL); + svp->created &= ~SVP_CREATED_THREAD; + } + + if (svp->connection) + { + mmal_connection_disable(svp->connection); + } + + mmal_port_disable(svp->video_output); +} + +/* Get stats since last call to svp_start() */ +void svp_get_stats(SVP_T *svp, SVP_STATS_T *stats) +{ + vcos_mutex_lock(&svp->mutex); + *stats = svp->stats; + vcos_mutex_unlock(&svp->mutex); +} + +/** Timer callback - stops playback */ +static void svp_timer_cb(void *ctx) +{ + SVP_T *svp = ctx; + svp_set_stop(svp, SVP_STOP_TIMEUP); + vcos_semaphore_post(&svp->sema); +} + +/** Watchdog timer callback - stops playback */ +static void svp_watchdog_cb(void *ctx) +{ + SVP_T *svp = ctx; + LOG_ERROR("%s: no frames received for %d ms, aborting", svp->video_output->name, + SVP_WATCHDOG_TIMEOUT_MS); + svp_set_stop(svp, SVP_STOP_ERROR); + vcos_semaphore_post(&svp->sema); +} + +/** Enable MMAL port, setting SVP instance as port userdata. */ +static MMAL_STATUS_T svp_port_enable(SVP_T *svp, MMAL_PORT_T *port, MMAL_PORT_BH_CB_T cb) +{ + port->userdata = (struct MMAL_PORT_USERDATA_T *)svp; + return mmal_port_enable(port, cb); +} + +/** Process decoded buffers queued by video decoder output callback */ +static void svp_process_returned_bufs(SVP_T *svp) +{ + SVP_CALLBACKS_T *callbacks = &svp->callbacks; + MMAL_BUFFER_HEADER_T *buf; + + while ((buf = mmal_queue_get(svp->queue)) != NULL) + { + if ((svp_get_stop(svp) & SVP_STOP_ERROR) == 0) + { + callbacks->video_frame_cb(callbacks->ctx, buf->data); + } + + svp->stats.video_frame_count++; + mmal_buffer_header_release(buf); + } +} + +/** Worker thread. Ensures video decoder output is supplied with buffers and sends decoded frames + * via user-supplied callback. + * @param arg Pointer to SVP instance. + * @return NULL always. + */ +static void *svp_worker(void *arg) +{ + SVP_T *svp = arg; + MMAL_PORT_T *video_output = svp->video_output; + SVP_CALLBACKS_T *callbacks = &svp->callbacks; + MMAL_BUFFER_HEADER_T *buf; + MMAL_STATUS_T st; + uint32_t stop; + + while (svp_get_stop(svp) == 0) + { + /* Send empty buffers to video decoder output port */ + while ((buf = mmal_queue_get(svp->out_pool->queue)) != NULL) + { + st = mmal_port_send_buffer(video_output, buf); + if (st != MMAL_SUCCESS) + { + LOG_ERROR("Failed to send buffer to %s", video_output->name); + } + } + + /* Process returned buffers */ + svp_process_returned_bufs(svp); + + /* Block for buffer release */ + vcos_semaphore_wait(&svp->sema); + } + + /* Might have the last few buffers queued */ + svp_process_returned_bufs(svp); + + /* Notify caller if we stopped unexpectedly */ + stop = svp_get_stop(svp); + LOG_TRACE("Worker thread exiting: stop=0x%x", (unsigned)stop); + callbacks->stop_cb(callbacks->ctx, stop); + + return NULL; +} + +/** Callback from a control port. */ +static void svp_bh_control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) +{ + SVP_T *svp = (SVP_T *)port->userdata; + + switch (buf->cmd) + { + case MMAL_EVENT_EOS: + LOG_TRACE("%s: EOS", port->name); + svp_set_stop(svp, SVP_STOP_EOS); + break; + + case MMAL_EVENT_ERROR: + LOG_ERROR("%s: MMAL error: %s", port->name, + mmal_status_to_string(*(MMAL_STATUS_T *)buf->data)); + svp_set_stop(svp, SVP_STOP_ERROR); + break; + + default: + LOG_TRACE("%s: buf %p, event %4.4s", port->name, buf, (char *)&buf->cmd); + break; + } + + mmal_buffer_header_release(buf); + + vcos_semaphore_post(&svp->sema); +} + +/** Callback from video decode output port. */ +static void svp_bh_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) +{ + SVP_T *svp = (SVP_T *)port->userdata; + + if (buf->length == 0) + { + LOG_TRACE("%s: zero-length buffer => EOS", port->name); + svp_set_stop(svp, SVP_STOP_EOS); // This shouldn't be necessary, but it is ... + mmal_buffer_header_release(buf); + } + else if (buf->data == NULL) + { + LOG_ERROR("%s: zero buffer handle", port->name); + mmal_buffer_header_release(buf); + } + else + { + /* Reset watchdog timer */ + vcos_timer_set(&svp->wd_timer, SVP_WATCHDOG_TIMEOUT_MS); + + /* Enqueue the decoded frame so we can return quickly to MMAL core */ + mmal_queue_put(svp->queue, buf); + } + + /* Notify worker */ + vcos_semaphore_post(&svp->sema); +} + +/** Reset svp->stop to 0, with locking. */ +static void svp_reset_stop(SVP_T *svp) +{ + vcos_mutex_lock(&svp->mutex); + svp->stop = 0; + vcos_mutex_unlock(&svp->mutex); +} + +/** Set additional flags in svp->stop, with locking. */ +static void svp_set_stop(SVP_T *svp, uint32_t stop_flags) +{ + vcos_mutex_lock(&svp->mutex); + svp->stop |= stop_flags; + vcos_mutex_unlock(&svp->mutex); +} + +/** Get value of svp->stop, with locking. */ +static uint32_t svp_get_stop(SVP_T *svp) +{ + uint32_t stop; + + vcos_mutex_lock(&svp->mutex); + stop = svp->stop; + vcos_mutex_unlock(&svp->mutex); + + return stop; +} diff --git a/host_applications/android/apps/vidtex/svp.h b/host_applications/android/apps/vidtex/svp.h new file mode 100755 index 0000000..80c2151 --- /dev/null +++ b/host_applications/android/apps/vidtex/svp.h @@ -0,0 +1,146 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SVP_H +#define SVP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + * Simple video player using MMAL. + * Uses MMAL container reader plus video decode component, and provides callback to retrieve + * buffers of decoded video frames. + * + * Thread-safety: The public API functions must be called from the same thread for a given SVP_T + * instance. The user is notified of decoded frames and other events from a separate + * thread, started by svp_start(). + */ + +/** Flags indicating reason for processing stop */ +/** Stop on to user request */ +#define SVP_STOP_USER (1 << 0) +/** Stop on end-of-stream */ +#define SVP_STOP_EOS (1 << 1) +/** Stop on error */ +#define SVP_STOP_ERROR (1 << 2) +/** Stop on timer */ +#define SVP_STOP_TIMEUP (1 << 3) + +/** + * Callback functions and context for player to communicate asynchronous data + * and events to user. + */ +typedef struct SVP_CALLBACKS_T +{ + /** Private pointer specified by caller, passed to callback functions. + * Not examined by svp_ functions, so may have any value, including NULL. + */ + void *ctx; + + /** Callback for decoded video frames. + * The buffer is released when this function returns, so the callback must either have processed + * the buffer or acquired a reference before returning. + * @param ctx Caller's private context, as specified by SVP_CALLBACKS_T::ctx. + * @param ob MMAL opaque buffer handle. + */ + void (*video_frame_cb)(void *ctx, void *buf); + + /** Callback for end of processing. + * @param ctx Caller's private context, as specified by SVP_CALLBACKS_T::ctx. + * @param reason Bitmask of SVP_STOP_XXX values giving reason for stopping. + */ + void (*stop_cb)(void *ctx, uint32_t stop_reason); +} SVP_CALLBACKS_T; + +/** Options to SVP playback */ +typedef struct SVP_OPTS_T +{ + /** Duration of playback, in milliseconds. 0 = default, which is full duration for media and + * a limited duration for camera. + */ + unsigned duration_ms; +} SVP_OPTS_T; + +/** Playback stats */ +typedef struct SVP_STATS_T +{ + /** Total number of video frames processed since the last call to svp_start(). + * 0 if svp_start() has never been called. */ + unsigned video_frame_count; +} SVP_STATS_T; + +/** Simple Video Player instance. Opaque structure. */ +typedef struct SVP_T SVP_T; + +/** + * Create a simple video player instance. + * @param uri URI to media, or NULL to use camera preview. + * @param callbacks Callbacks for caller to receive notification of events, + * such as decoded buffers. + * @param opts Player options. + * @return Newly created simple video player instance, or NULL on error. + */ +SVP_T *svp_create(const char *uri, SVP_CALLBACKS_T *callbacks, const SVP_OPTS_T *opts); + +/** + * Destroy a simple video player instance. + * @param svp Simple video player instance. May be NULL, in which case this + * function does nothing. + */ +void svp_destroy(SVP_T *svp); + +/** + * Start a simple video player instance. + * Decoded frames are returned to the SVP_CALLBACKS_T::video_frame_cb function + * passed to svp_create(). + * @param svp Simple video player instance. + * @return 0 on success; -1 on failure. + */ +int svp_start(SVP_T *svp); + +/** + * Stop a simple video player instance. + * If the player is not running, then this function does nothing. + * @param svp Simple video player instance. + */ +void svp_stop(SVP_T *svp); + +/** + * Get player stats. + * @param svp Simple video player instance. + * @param stats Buffer in which stats are returned. + */ +void svp_get_stats(SVP_T *svp, SVP_STATS_T *stats); + + +#ifdef __cplusplus +} +#endif + +#endif /* SVP_H */ diff --git a/host_applications/android/apps/vidtex/vidtex.c b/host_applications/android/apps/vidtex/vidtex.c new file mode 100755 index 0000000..c72a78c --- /dev/null +++ b/host_applications/android/apps/vidtex/vidtex.c @@ -0,0 +1,555 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include "applog.h" +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_stdbool.h" +#include "interface/khronos/include/EGL/eglext_brcm.h" +#include "vidtex.h" + +/** Max number of simultaneous EGL images supported = max number of distinct video decoder + * buffers. + */ +#define VT_MAX_IMAGES 32 + +/** Maximum permitted difference between the number of EGL buffer swaps and number of video + * frames. + */ +#define VT_MAX_FRAME_DISPARITY 2 + +/** Mapping of MMAL opaque buffer handle to EGL image */ +typedef struct VIDTEX_IMAGE_SLOT_T +{ + /* Decoded video frame, as MMAL opaque buffer handle. NULL => unused slot. */ + void *video_frame; + + /* Corresponding EGL image */ + EGLImageKHR image; +} VIDTEX_IMAGE_SLOT_T; + +/** + * Video Texture. Displays video from a URI or camera onto an EGL surface. + */ +typedef struct VIDTEX_T +{ + /** Test options; bitmask of VIDTEX_OPT_XXX values */ + uint32_t opts; + + /* Semaphore to synchronise use of decoded frame (video_frame). */ + VCOS_SEMAPHORE_T sem_decoded; + + /* Semaphore to synchronise drawing of video frame. */ + VCOS_SEMAPHORE_T sem_drawn; + + /* Mutex to guard access to quit field. */ + VCOS_MUTEX_T mutex; + + /* Signal thread to quit. */ + bool quit; + + /* Reason for quitting */ + uint32_t stop_reason; + + /* EGL display/surface/context on which to render the video */ + EGLDisplay display; + EGLSurface surface; + EGLContext context; + + /* EGL texture name */ + GLuint texture; + + /* Table of EGL images corresponding to MMAL opaque buffer handles */ + VIDTEX_IMAGE_SLOT_T slots[VT_MAX_IMAGES]; + + /* Current decoded video frame, as MMAL opaque buffer handle. + * NULL if no buffer currently available. */ + void *video_frame; + + /* Number of EGL buffer swaps */ + unsigned num_swaps; +} VIDTEX_T; + +/* Vertex co-ordinates: + * + * v0----v1 + * | | + * | | + * | | + * v3----v2 + */ + +static const GLfloat vt_vertices[] = +{ +#define VT_V0 -0.8, 0.8, 0.8, +#define VT_V1 0.8, 0.8, 0.8, +#define VT_V2 0.8, -0.8, 0.8, +#define VT_V3 -0.8, -0.8, 0.8, + VT_V0 VT_V3 VT_V2 VT_V2 VT_V1 VT_V0 +}; + +/* Texture co-ordinates: + * + * (0,0) b--c + * | | + * a--d + * + * b,a,d d,c,b + */ +static const GLfloat vt_tex_coords[] = +{ + 0, 0, 0, 1, 1, 1, + 1, 1, 1, 0, 0, 0 +}; + +/* Local function prototypes */ +static VIDTEX_T *vidtex_create(EGLNativeWindowType win); +static void vidtex_destroy(VIDTEX_T *vt); +static int vidtex_gl_init(VIDTEX_T *vt, EGLNativeWindowType win); +static void vidtex_gl_term(VIDTEX_T *vt); +static void vidtex_destroy_images(VIDTEX_T *vt); +static int vidtex_play(VIDTEX_T *vt, const VIDTEX_PARAMS_T *params); +static void vidtex_check_gl(VIDTEX_T *vt, uint32_t line); +static void vidtex_draw(VIDTEX_T *vt, void *video_frame); +static void vidtex_flush_gl(VIDTEX_T *vt); +static bool vidtex_set_quit(VIDTEX_T *vt, bool quit); +static void vidtex_video_frame_cb(void *ctx, void *ob); +static void vidtex_stop_cb(void *ctx, uint32_t stop_reason); + +#define VIDTEX_CHECK_GL(VT) vidtex_check_gl(VT, __LINE__) + +/** Create a new vidtex instance */ +static VIDTEX_T *vidtex_create(EGLNativeWindowType win) +{ + VIDTEX_T *vt; + VCOS_STATUS_T st; + + vt = vcos_calloc(1, sizeof(*vt), "vidtex"); + if (vt == NULL) + { + vcos_log_trace("Memory allocation failure"); + return NULL; + } + + st = vcos_semaphore_create(&vt->sem_decoded, "vidtex-dec", 0); + if (st != VCOS_SUCCESS) + { + vcos_log_trace("Error creating semaphore"); + goto error_ctx; + } + + st = vcos_semaphore_create(&vt->sem_drawn, "vidtex-drw", 0); + if (st != VCOS_SUCCESS) + { + vcos_log_trace("Error creating semaphore"); + goto error_sem1; + } + + st = vcos_mutex_create(&vt->mutex, "vidtex"); + if (st != VCOS_SUCCESS) + { + vcos_log_trace("Error creating semaphore"); + goto error_sem2; + } + + if (vidtex_gl_init(vt, win) != 0) + { + vcos_log_trace("Error initialising EGL"); + goto error_mutex; + } + + vt->quit = false; + vt->stop_reason = 0; + + return vt; + +error_mutex: + vcos_mutex_delete(&vt->mutex); +error_sem2: + vcos_semaphore_delete(&vt->sem_drawn); +error_sem1: + vcos_semaphore_delete(&vt->sem_decoded); +error_ctx: + vcos_free(vt); + return NULL; +} + +/** Destroy a vidtex instance */ +static void vidtex_destroy(VIDTEX_T *vt) +{ + vidtex_gl_term(vt); + vcos_mutex_delete(&vt->mutex); + vcos_semaphore_delete(&vt->sem_drawn); + vcos_semaphore_delete(&vt->sem_decoded); + vcos_free(vt); +} + +/** Init GL using a native window */ +static int vidtex_gl_init(VIDTEX_T *vt, EGLNativeWindowType win) +{ + const EGLint attribs[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_NONE + }; + EGLConfig config; + EGLint num_configs; + + vt->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(vt->display, 0, 0); + + eglChooseConfig(vt->display, attribs, &config, 1, &num_configs); + + vt->surface = eglCreateWindowSurface(vt->display, config, win, NULL); + vt->context = eglCreateContext(vt->display, config, NULL, NULL); + + if (!eglMakeCurrent(vt->display, vt->surface, vt->surface, vt->context)) + { + vidtex_gl_term(vt); + return -1; + } + + glGenTextures(1, &vt->texture); + + glShadeModel(GL_FLAT); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glEnable(GL_TEXTURE_EXTERNAL_OES); + glDisable(GL_TEXTURE_2D); + + return 0; +} + +/** Terminate GL */ +static void vidtex_gl_term(VIDTEX_T *vt) +{ + vidtex_destroy_images(vt); + + /* Delete texture name */ + glDeleteTextures(1, &vt->texture); + + /* Terminate EGL */ + eglMakeCurrent(vt->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(vt->display, vt->context); + eglDestroySurface(vt->display, vt->surface); + eglTerminate(vt->display); +} + +/** Destroy all EGL images */ +static void vidtex_destroy_images(VIDTEX_T *vt) +{ + VIDTEX_IMAGE_SLOT_T *slot; + + for (slot = vt->slots; slot < vt->slots + vcos_countof(vt->slots); slot++) + { + slot->video_frame = NULL; + + if (slot->image) + { + vcos_log_trace("Destroying EGL image %p", slot->image); + eglDestroyImageKHR(vt->display, slot->image); + slot->image = NULL; + } + } +} + +/** Play video - from URI or camera - on EGL surface. */ +static int vidtex_play(VIDTEX_T *vt, const VIDTEX_PARAMS_T *params) +{ + const char *uri; + SVP_CALLBACKS_T callbacks; + SVP_T *svp; + SVP_OPTS_T opts; + SVP_STATS_T stats; + int rv = -1; + + uri = (params->uri[0] == '\0') ? NULL : params->uri; + vt->opts = params->opts; + callbacks.ctx = vt; + callbacks.video_frame_cb = vidtex_video_frame_cb; + callbacks.stop_cb = vidtex_stop_cb; + opts.duration_ms = params->duration_ms; + + svp = svp_create(uri, &callbacks, &opts); + if (svp) + { + /* Reset stats */ + vt->num_swaps = 0; + + /* Run video player until receive quit notification */ + if (svp_start(svp) == 0) + { + while (!vidtex_set_quit(vt, false)) + { + vcos_semaphore_wait(&vt->sem_decoded); + + if (vt->video_frame) + { + vidtex_draw(vt, vt->video_frame); + vcos_semaphore_post(&vt->sem_drawn); + } + } + + vcos_semaphore_post(&vt->sem_drawn); + + /* Dump stats */ + svp_get_stats(svp, &stats); + vcos_log_info("video frames decoded: %6u", stats.video_frame_count); + vcos_log_info("EGL buffer swaps: %6u", vt->num_swaps); + + /* Determine status of operation and log errors */ + if (vt->stop_reason & SVP_STOP_ERROR) + { + vcos_log_error("vidtex exiting on error"); + } + else if (vt->num_swaps == 0) + { + vcos_log_error("vidtex completed with no EGL buffer swaps"); + } + else if (abs((int)vt->num_swaps - (int)stats.video_frame_count) > VT_MAX_FRAME_DISPARITY) + { + vcos_log_error("vidtex completed with %u EGL buffer swaps, but %u video frames", + vt->num_swaps, (int)stats.video_frame_count); + } + else + { + rv = 0; + } + } + + svp_destroy(svp); + } + + vidtex_flush_gl(vt); + + return rv; +} + +/** Check for OpenGL errors - logs any errors and sets quit flag */ +static void vidtex_check_gl(VIDTEX_T *vt, uint32_t line) +{ + GLenum error = glGetError(); + int abort = 0; + while (error != GL_NO_ERROR) + { + vcos_log_error("GL error: line %d error 0x%04x", line, error); + abort = 1; + error = glGetError(); + } + if (abort) + vidtex_stop_cb(vt, SVP_STOP_ERROR); +} + +/* Draw one video frame onto EGL surface. + * @param vt vidtex instance. + * @param video_frame MMAL opaque buffer handle for decoded video frame. Can't be NULL. + */ +static void vidtex_draw(VIDTEX_T *vt, void *video_frame) +{ + EGLImageKHR image; + VIDTEX_IMAGE_SLOT_T *slot; + static uint32_t frame_num = 0; + + vcos_assert(video_frame); + + glClearColor(0, 0, 0, 0); + glClearDepthf(1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + glBindTexture(GL_TEXTURE_EXTERNAL_OES, vt->texture); + VIDTEX_CHECK_GL(vt); + + /* Lookup or create EGL image corresponding to supplied buffer handle. + * N.B. Slot array is filled in sequentially, with the images all destroyed together on + * vidtex termination; it never has holes. */ + image = EGL_NO_IMAGE_KHR; + + for (slot = vt->slots; slot < vt->slots + vcos_countof(vt->slots); slot++) + { + if (slot->video_frame == video_frame) + { + vcos_assert(slot->image); + image = slot->image; + break; + } + + if (slot->video_frame == NULL) + { + EGLenum target; + vcos_assert(slot->image == NULL); + + if (vt->opts & VIDTEX_OPT_Y_TEXTURE) + target = EGL_IMAGE_BRCM_MULTIMEDIA_Y; + else if (vt->opts & VIDTEX_OPT_U_TEXTURE) + target = EGL_IMAGE_BRCM_MULTIMEDIA_U; + else if (vt->opts & VIDTEX_OPT_V_TEXTURE) + target = EGL_IMAGE_BRCM_MULTIMEDIA_V; + else + target = EGL_IMAGE_BRCM_MULTIMEDIA; + + image = eglCreateImageKHR(vt->display, EGL_NO_CONTEXT, target, + (EGLClientBuffer)video_frame, NULL); + if (image == EGL_NO_IMAGE_KHR) + { + vcos_log_error("EGL image conversion error"); + } + else + { + vcos_log_trace("Created EGL image %p for buf %p", image, video_frame); + slot->video_frame = video_frame; + slot->image = image; + } + VIDTEX_CHECK_GL(vt); + + break; + } + } + + if (slot == vt->slots + vcos_countof(vt->slots)) + { + vcos_log_error("Exceeded configured max number of EGL images"); + } + + /* Draw the EGL image */ + if (image != EGL_NO_IMAGE_KHR) + { + /* Assume 30fps */ + int frames_per_rev = 30 * 15; + GLfloat angle = (frame_num * 360) / (GLfloat) frames_per_rev; + frame_num = (frame_num + 1) % frames_per_rev; + + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); + VIDTEX_CHECK_GL(vt); + + glRotatef(angle, 0.0, 0.0, 1.0); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, vt_vertices); + glDisableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, vt_tex_coords); + + glDrawArrays(GL_TRIANGLES, 0, vcos_countof(vt_tex_coords) / 2); + + eglSwapBuffers(vt->display, vt->surface); + + if (vt->opts & VIDTEX_OPT_IMG_PER_FRAME) + { + vidtex_destroy_images(vt); + } + + vt->num_swaps++; + } + + VIDTEX_CHECK_GL(vt); +} + +/** Do some GL stuff in order to ensure that any multimedia-related GL buffers have been released + * if they are going to be released. + */ +static void vidtex_flush_gl(VIDTEX_T *vt) +{ + int i; + + glFlush(); + glClearColor(0, 0, 0, 0); + + for (i = 0; i < 10; i++) + { + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(vt->display, vt->surface); + VIDTEX_CHECK_GL(vt); + } + + glFlush(); + VIDTEX_CHECK_GL(vt); +} + +/** Set quit flag, with locking. + * @param quit New value of the quit flag: true - command thread to quit; false - command thread + * to continue. + * @return Old value of the quit flag. + */ +static bool vidtex_set_quit(VIDTEX_T *vt, bool quit) +{ + vcos_mutex_lock(&vt->mutex); + bool old_quit = vt->quit; + vt->quit = quit; + vcos_mutex_unlock(&vt->mutex); + + return old_quit; +} + +/** Callback to receive decoded video frame */ +static void vidtex_video_frame_cb(void *ctx, void *ob) +{ + if (ob) + { + VIDTEX_T *vt = ctx; + /* coverity[missing_lock] Coverity gets confused by the semaphore locking scheme */ + vt->video_frame = ob; + vcos_semaphore_post(&vt->sem_decoded); + vcos_semaphore_wait(&vt->sem_drawn); + vt->video_frame = NULL; + } +} + +/** Callback to receive stop notification. Sets quit flag and posts semaphore. + * @param ctx VIDTEX_T instance. Declared as void * in order to use as SVP callback. + * @param stop_reason SVP stop reason. + */ +static void vidtex_stop_cb(void *ctx, uint32_t stop_reason) +{ + VIDTEX_T *vt = ctx; + vt->stop_reason = stop_reason; + vidtex_set_quit(vt, true); + vcos_semaphore_post(&vt->sem_decoded); +} + +/* Convenience function to create/play/destroy */ +int vidtex_run(const VIDTEX_PARAMS_T *params, EGLNativeWindowType win) +{ + VIDTEX_T *vt; + int rv = -1; + + vt = vidtex_create(win); + if (vt) + { + rv = vidtex_play(vt, params); + vidtex_destroy(vt); + } + + return rv; +} diff --git a/host_applications/android/apps/vidtex/vidtex.h b/host_applications/android/apps/vidtex/vidtex.h new file mode 100755 index 0000000..4a55846 --- /dev/null +++ b/host_applications/android/apps/vidtex/vidtex.h @@ -0,0 +1,89 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VIDTEX_H +#define VIDTEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file Video-on-texture display. + * Displays video decoded from a media URI or from camera preview onto an EGL surface. + */ +#include +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_stdint.h" +#include "svp.h" + +/** Max length of a vidtex URI */ +#define VIDTEX_MAX_URI 256 + +/** Test option - create + destroy EGL image every frame */ +#define VIDTEX_OPT_IMG_PER_FRAME (1 << 0) + +/** Test option - display the Y' plane as greyscale texture */ +#define VIDTEX_OPT_Y_TEXTURE (1 << 1) + +/** Test option - display the U plane as greyscale texture */ +#define VIDTEX_OPT_U_TEXTURE (1 << 2) + +/** Test option - display the V plane as greyscale texture */ +#define VIDTEX_OPT_V_TEXTURE (1 << 3) + +/** Parameters to vidtex run. + * N.B. Types need to be bitwise copyable, and between C and C++, so don't use pointers, bool + * or anything else liable for problems. + */ +typedef struct VIDTEX_PARAMS_T +{ + /** Duration of playback, in milliseconds. 0 = default, which is full duration for media and + * a limited duration for camera. + */ + uint32_t duration_ms; + + /** Media URI, or empty string to use camera preview */ + char uri[VIDTEX_MAX_URI]; + + /** Test options; bitmask of VIDTEX_OPT_XXX values */ + uint32_t opts; +} VIDTEX_PARAMS_T; + +/** Run video-on-texture. + * @param params Parameters to video-on-texture display. + * @param win Native window handle. + * @return 0 on success; -1 on failure. + * It is considered successful if the video decode completed without error and at least + * one EGL image was shown. + */ +int vidtex_run(const VIDTEX_PARAMS_T *params, EGLNativeWindowType win); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/host_applications/framework/common/host_ilcore.h b/host_applications/framework/common/host_ilcore.h new file mode 100755 index 0000000..d91a240 --- /dev/null +++ b/host_applications/framework/common/host_ilcore.h @@ -0,0 +1,79 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef HOST_ILCORE_H +#define HOST_ILCORE_H + +#ifdef WANT_OMX_NAME_MANGLE +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_Init(void); +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_Deinit(void); +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_ComponentNameEnum( + OMX_OUT OMX_STRING cComponentName, + OMX_IN OMX_U32 nNameLength, + OMX_IN OMX_U32 nIndex); +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_GetHandle( + OMX_OUT OMX_HANDLETYPE* pHandle, + OMX_IN OMX_STRING cComponentName, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_CALLBACKTYPE* pCallBacks); +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_FreeHandle( + OMX_IN OMX_HANDLETYPE hComponent); +OMX_API OMX_ERRORTYPE OMX_APIENTRY host_OMX_SetupTunnel( + OMX_IN OMX_HANDLETYPE hOutput, + OMX_IN OMX_U32 nPortOutput, + OMX_IN OMX_HANDLETYPE hInput, + OMX_IN OMX_U32 nPortInput); +OMX_API OMX_ERRORTYPE host_OMX_GetComponentsOfRole ( + OMX_IN OMX_STRING role, + OMX_INOUT OMX_U32 *pNumComps, + OMX_INOUT OMX_U8 **compNames); +OMX_API OMX_ERRORTYPE host_OMX_GetRolesOfComponent ( + OMX_IN OMX_STRING compName, + OMX_INOUT OMX_U32 *pNumRoles, + OMX_OUT OMX_U8 **roles); +OMX_ERRORTYPE host_OMX_GetDebugInformation ( + OMX_OUT OMX_STRING debugInfo, + OMX_INOUT OMX_S32 *pLen); + +#define OMX_Init host_OMX_Init +#define OMX_Deinit host_OMX_Deinit +#define OMX_ComponentNameEnum host_OMX_ComponentNameEnum +#define OMX_GetHandle host_OMX_GetHandle +#define OMX_FreeHandle host_OMX_FreeHandle +#define OMX_SetupTunnel host_OMX_SetupTunnel +#define OMX_GetComponentsOfRole host_OMX_GetComponentsOfRole +#define OMX_GetRolesOfComponent host_OMX_GetRolesOfComponent +#define OMX_GetDebugInformation host_OMX_GetDebugInformation +#else +OMX_ERRORTYPE OMX_GetDebugInformation ( + OMX_OUT OMX_STRING debugInfo, + OMX_INOUT OMX_S32 *pLen); +#endif + +#endif // HOST_ILCORE_H + + diff --git a/host_applications/framework/common/ilcore.c b/host_applications/framework/common/ilcore.c new file mode 100755 index 0000000..ebd13cc --- /dev/null +++ b/host_applications/framework/common/ilcore.c @@ -0,0 +1,353 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include + +//includes +#include +#include +#include +#include + +#include "interface/vmcs_host/khronos/IL/OMX_Component.h" +#include "interface/vmcs_host/vcilcs.h" +#include "interface/vmcs_host/vchost.h" +#include "interface/vmcs_host/vcilcs_common.h" + +#include "host_ilcore.h" + + +#ifdef WANT_OMX_NAME_MANGLE +#define OMX_Deinit host_OMX_Deinit +#define OMX_Init host_OMX_Init +#define OMX_SetupTunnel host_OMX_SetupTunnel +#define OMX_FreeHandle host_OMX_FreeHandle +#define OMX_GetHandle host_OMX_GetHandle +#define OMX_GetRolesOfComponent host_OMX_GetRolesOfComponent +#define OMX_GetComponentsOfRole host_OMX_GetComponentsOfRole +#define OMX_ComponentNameEnum host_OMX_ComponentNameEnum +#define OMX_GetDebugInformation host_OMX_GetDebugInformation +#endif + +#ifdef WANT_LOCAL_OMX + // xxx: we need to link to local OpenMAX IL core as well + OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_Init(void); + OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_Deinit(void); + OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_GetHandle( + OMX_OUT OMX_HANDLETYPE* pHandle, + OMX_IN OMX_STRING cComponentName, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_CALLBACKTYPE* pCallBacks); + OMX_API OMX_ERRORTYPE OMX_APIENTRY vc_OMX_FreeHandle( + OMX_IN OMX_HANDLETYPE hComponent); +#endif + +static int coreInit = 0; +static int nActiveHandles = 0; +static ILCS_SERVICE_T *ilcs_service = NULL; +static VCOS_MUTEX_T lock; +static VCOS_ONCE_T once = VCOS_ONCE_INIT; + +/* Atomic creation of lock protecting shared state */ +static void initOnce(void) +{ + VCOS_STATUS_T status; + status = vcos_mutex_create(&lock, VCOS_FUNCTION); + vcos_demand(status == VCOS_SUCCESS); +} + +/* OMX_Init */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void) +{ + VCOS_STATUS_T status; + OMX_ERRORTYPE err = OMX_ErrorNone; + + status = vcos_once(&once, initOnce); + vcos_demand(status == VCOS_SUCCESS); + + vcos_mutex_lock(&lock); + +#ifdef WANT_LOCAL_OMX + vc_OMX_Init(); // initialise local core first +#endif + + if(coreInit == 0) + { + // we need to connect via an ILCS connection to VideoCore + VCHI_INSTANCE_T initialise_instance; + VCHI_CONNECTION_T *connection; + ILCS_CONFIG_T config; + + vc_host_get_vchi_state(&initialise_instance, &connection); + + vcilcs_config(&config); +#ifdef USE_VCHIQ_ARM + ilcs_service = ilcs_init((VCHIQ_INSTANCE_T) initialise_instance, (void **) &connection, &config, 0); +#else + ilcs_service = ilcs_init((VCHIQ_STATE_T *) initialise_instance, (void **) &connection, &config, 0); +#endif + + if(ilcs_service == NULL) + { + err = OMX_ErrorHardware; + goto end; + } + + coreInit = 1; + } +#ifdef USE_VCHIQ_ARM + else + coreInit++; +#endif + +end: + vcos_mutex_unlock(&lock); + return err; +} + +/* OMX_Deinit */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void) +{ + if(coreInit == 0) // || (coreInit == 1 && nActiveHandles > 0)) + return OMX_ErrorNotReady; + + vcos_mutex_lock(&lock); + +#ifdef USE_VCHIQ_ARM + coreInit--; +#endif + + if(coreInit == 0) + { + // we need to teardown the ILCS connection to VideoCore + ilcs_deinit(ilcs_service); + ilcs_service = NULL; + } + +#ifdef WANT_LOCAL_OMX + vc_OMX_Deinit(); // deinitialise local core as well +#endif + + vcos_mutex_unlock(&lock); + + return OMX_ErrorNone; +} + + +/* OMX_ComponentNameEnum */ +OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum( + OMX_OUT OMX_STRING cComponentName, + OMX_IN OMX_U32 nNameLength, + OMX_IN OMX_U32 nIndex) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_component_name_enum(ilcs_get_common(ilcs_service), cComponentName, nNameLength, nIndex); +} + + +/* OMX_GetHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle( + OMX_OUT OMX_HANDLETYPE* pHandle, + OMX_IN OMX_STRING cComponentName, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_CALLBACKTYPE* pCallBacks) +{ + OMX_ERRORTYPE eError; + OMX_COMPONENTTYPE *pComp; + OMX_HANDLETYPE hHandle = 0; + + if (pHandle == NULL || cComponentName == NULL || pCallBacks == NULL || ilcs_service == NULL) + { + if(pHandle) + *pHandle = NULL; + return OMX_ErrorBadParameter; + } + +#if defined(WANT_LOCAL_OMX) && 0 + if ((eError = vc_OMX_GetHandle(pHandle, cComponentName, pAppData, pCallBacks)) != OMX_ErrorNone) +#endif + { + pComp = (OMX_COMPONENTTYPE *)malloc(sizeof(OMX_COMPONENTTYPE)); + if (!pComp) + { + vcos_assert(0); + return OMX_ErrorInsufficientResources; + } + memset(pComp, 0, sizeof(OMX_COMPONENTTYPE)); + hHandle = (OMX_HANDLETYPE)pComp; + pComp->nSize = sizeof(OMX_COMPONENTTYPE); + pComp->nVersion.nVersion = OMX_VERSION; + eError = vcil_out_create_component(ilcs_get_common(ilcs_service), hHandle, cComponentName); + + if (eError == OMX_ErrorNone) { + // Check that all function pointers have been filled in. + // All fields should be non-zero. + int i; + uint32_t *p = (uint32_t *) pComp; + for(i=0; i>2; i++) + if(*p++ == 0) + eError = OMX_ErrorInvalidComponent; + + if(eError != OMX_ErrorNone && pComp->ComponentDeInit) + pComp->ComponentDeInit(hHandle); + } + + if (eError == OMX_ErrorNone) { + eError = pComp->SetCallbacks(hHandle,pCallBacks,pAppData); + if (eError != OMX_ErrorNone) + pComp->ComponentDeInit(hHandle); + } + if (eError == OMX_ErrorNone) { + *pHandle = hHandle; + } + else { + *pHandle = NULL; + free(pComp); + } + } + + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + nActiveHandles++; + vcos_mutex_unlock(&lock); + } + + return eError; +} + +/* OMX_FreeHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle( + OMX_IN OMX_HANDLETYPE hComponent) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pComp; + + if (hComponent == NULL || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + pComp = (OMX_COMPONENTTYPE*)hComponent; + +#ifdef WANT_LOCAL_OMX + // xxx: a bit of a bodge, we rely on knowing that + // the local core doesn't make use of this field but + // ILCS does... + if (pComp->pApplicationPrivate == NULL) + return vc_OMX_FreeHandle(hComponent); +#endif + + if (ilcs_service == NULL) + return OMX_ErrorBadParameter; + + eError = (pComp->ComponentDeInit)(hComponent); + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + --nActiveHandles; + vcos_mutex_unlock(&lock); + free(pComp); + } + + vcos_assert(nActiveHandles >= 0); + + return eError; +} + +/* OMX_SetupTunnel */ +OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel( + OMX_IN OMX_HANDLETYPE hOutput, + OMX_IN OMX_U32 nPortOutput, + OMX_IN OMX_HANDLETYPE hInput, + OMX_IN OMX_U32 nPortInput) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pCompIn, *pCompOut; + OMX_TUNNELSETUPTYPE oTunnelSetup; + + if ((hOutput == NULL && hInput == NULL) || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + oTunnelSetup.nTunnelFlags = 0; + oTunnelSetup.eSupplier = OMX_BufferSupplyUnspecified; + + pCompOut = (OMX_COMPONENTTYPE*)hOutput; + + if (hOutput){ + eError = pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, hInput, nPortInput, &oTunnelSetup); + } + + if (eError == OMX_ErrorNone && hInput) { + pCompIn = (OMX_COMPONENTTYPE*)hInput; + eError = pCompIn->ComponentTunnelRequest(hInput, nPortInput, hOutput, nPortOutput, &oTunnelSetup); + + if (eError != OMX_ErrorNone && hOutput) { + /* cancel tunnel request on output port since input port failed */ + pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, NULL, 0, NULL); + } + } + return eError; +} + +/* OMX_GetComponentsOfRole */ +OMX_ERRORTYPE OMX_GetComponentsOfRole ( + OMX_IN OMX_STRING role, + OMX_INOUT OMX_U32 *pNumComps, + OMX_INOUT OMX_U8 **compNames) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumComps = 0; + return eError; +} + +/* OMX_GetRolesOfComponent */ +OMX_ERRORTYPE OMX_GetRolesOfComponent ( + OMX_IN OMX_STRING compName, + OMX_INOUT OMX_U32 *pNumRoles, + OMX_OUT OMX_U8 **roles) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumRoles = 0; + return eError; +} + +/* OMX_GetDebugInformation */ +OMX_ERRORTYPE OMX_GetDebugInformation ( + OMX_OUT OMX_STRING debugInfo, + OMX_INOUT OMX_S32 *pLen) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_get_debug_information(ilcs_get_common(ilcs_service), debugInfo, pLen); +} + + + +/* File EOF */ + diff --git a/host_applications/linux/CMakeLists.txt b/host_applications/linux/CMakeLists.txt new file mode 100755 index 0000000..80a1908 --- /dev/null +++ b/host_applications/linux/CMakeLists.txt @@ -0,0 +1,25 @@ +# linux apps + +if(NOT ARM64) + add_subdirectory(libs/bcm_host) + add_subdirectory(apps/hello_pi) +endif() +add_subdirectory(apps/gencmd) +add_subdirectory(apps/tvservice) +add_subdirectory(apps/vcmailbox) +if(NOT ARM64) + add_subdirectory(apps/raspicam) + add_subdirectory(libs/sm) + add_subdirectory(apps/smem) +endif() +add_subdirectory(libs/debug_sym) +add_subdirectory(apps/dtoverlay) +add_subdirectory(apps/dtmerge) + +if(ALL_APPS) + add_subdirectory(apps/vcdbg) + add_subdirectory(libs/elftoolchain) + # add_subdirectory(apps/smct) + add_subdirectory(apps/edid_parser) + add_subdirectory(apps/hello_pi) +endif() diff --git a/host_applications/linux/apps/dtmerge/CMakeLists.txt b/host_applications/linux/apps/dtmerge/CMakeLists.txt new file mode 100755 index 0000000..d173697 --- /dev/null +++ b/host_applications/linux/apps/dtmerge/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.8) + +get_filename_component (VIDEOCORE_ROOT ../../../.. ABSOLUTE) +include (${VIDEOCORE_ROOT}/makefiles/cmake/global_settings.cmake) + +if (NOT WIN32) + add_definitions(-Wall -Werror) +endif () + +include_directories ( + ${VIDEOCORE_HEADERS_BUILD_DIR} + ${VIDEOCORE_ROOT} + ${VIDEOCORE_ROOT}/opensrc/helpers/libfdt + ${VIDEOCORE_ROOT}/helpers/dtoverlay +) + +add_executable(dtmerge dtmerge.c) +target_link_libraries(dtmerge dtovl) + +install(TARGETS dtmerge RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/dtmerge/dtmerge.c b/host_applications/linux/apps/dtmerge/dtmerge.c new file mode 100755 index 0000000..9243da1 --- /dev/null +++ b/host_applications/linux/apps/dtmerge/dtmerge.c @@ -0,0 +1,172 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "dtoverlay.h" + +static void usage(void) +{ + printf("Usage:\n"); + printf(" dtmerge [ - [param=value] ...\n"); + printf(" to apply a parameter to the base dtb (like dtparam)\n"); + printf(" dtmerge [ [param=value] ...\n"); + printf(" to apply an overlay with parameters (like dtoverlay)\n"); + printf(" where is any of:\n"); + printf(" -d Enable debug output\n"); + printf(" -h Show this help message\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + const char *base_file; + const char *merged_file; + const char *overlay_file; + DTBLOB_T *base_dtb; + DTBLOB_T *overlay_dtb; + int err; + int argn = 1; + int max_dtb_size = 100000; + + while ((argn < argc) && (argv[argn][0] == '-')) + { + const char *arg = argv[argn++]; + if ((strcmp(arg, "-d") == 0) || + (strcmp(arg, "--debug") == 0)) + dtoverlay_enable_debug(1); + else if ((strcmp(arg, "-h") == 0) || + (strcmp(arg, "--help") == 0)) + usage(); + else + { + printf("* Unknown option '%s'\n", arg); + usage(); + } + } + + if (argc < (argn + 3)) + { + usage(); + } + + base_file = argv[argn++]; + merged_file = argv[argn++]; + overlay_file = argv[argn++]; + + base_dtb = dtoverlay_load_dtb(base_file, max_dtb_size); + if (!base_dtb) + { + printf("* failed to load '%s'\n", base_file); + return -1; + } + + err = dtoverlay_set_synonym(base_dtb, "i2c", "i2c0"); + err = dtoverlay_set_synonym(base_dtb, "i2c_arm", "i2c0"); + err = dtoverlay_set_synonym(base_dtb, "i2c_vc", "i2c1"); + err = dtoverlay_set_synonym(base_dtb, "i2c_baudrate", "i2c0_baudrate"); + err = dtoverlay_set_synonym(base_dtb, "i2c_arm_baudrate", "i2c0_baudrate"); + err = dtoverlay_set_synonym(base_dtb, "i2c_vc_baudrate", "i2c1_baudrate"); + + if (strcmp(overlay_file, "-") == 0) + { + overlay_dtb = base_dtb; + } + else + { + overlay_dtb = dtoverlay_load_dtb(overlay_file, max_dtb_size); + if (overlay_dtb) + err = dtoverlay_fixup_overlay(base_dtb, overlay_dtb); + else + err = -1; + } + + while (!err && (argn < argc)) + { + char *param_name = argv[argn++]; + char *param_value = param_name + strcspn(param_name, "="); + const void *override_data; + int data_len; + + if (*param_value == '=') + { + *(param_value++) = '\0'; + } + else + { + /* This isn't a well-formed parameter assignment, but it can be + treated as an assignment of true. */ + param_value = "true"; + } + + override_data = dtoverlay_find_override(overlay_dtb, param_name, + &data_len); + if (override_data) + { + err = dtoverlay_apply_override(overlay_dtb, param_name, + override_data, data_len, + param_value); + } + else + { + override_data = dtoverlay_find_override(base_dtb, param_name, &data_len); + if (override_data) + { + err = dtoverlay_apply_override(base_dtb, param_name, + override_data, data_len, + param_value); + } + else + { + printf("* unknown param '%s'\n", param_name); + err = data_len; + } + } + } + + if (!err && (overlay_dtb != base_dtb)) + { + err = dtoverlay_merge_overlay(base_dtb, overlay_dtb); + + dtoverlay_free_dtb(overlay_dtb); + } + + if (!err) + { + dtoverlay_pack_dtb(base_dtb); + err = dtoverlay_save_dtb(base_dtb, merged_file); + } + + dtoverlay_free_dtb(base_dtb); + + if (err != 0) + printf("* Exiting with error code %d\n", err); + + return err; +} diff --git a/host_applications/linux/apps/dtoverlay/CMakeLists.txt b/host_applications/linux/apps/dtoverlay/CMakeLists.txt new file mode 100755 index 0000000..9009200 --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 2.8) + +get_filename_component (VIDEOCORE_ROOT ../../../.. ABSOLUTE) +include (${VIDEOCORE_ROOT}/makefiles/cmake/global_settings.cmake) + +if (NOT WIN32) + add_definitions(-Wall -Werror) +endif () + +include_directories ( + ${VIDEOCORE_HEADERS_BUILD_DIR} + ${VIDEOCORE_ROOT} + ${VIDEOCORE_ROOT}/opensrc/helpers/libfdt + ${VIDEOCORE_ROOT}/helpers/dtoverlay +) + +add_executable(dtoverlay dtoverlay_main.c utils.c) +target_link_libraries(dtoverlay dtovl) +install(TARGETS dtoverlay RUNTIME DESTINATION bin) + +add_custom_command(TARGET dtoverlay POST_BUILD COMMAND ln;-sf;dtoverlay;dtparam) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dtparam DESTINATION bin) + +set(DTOVERLAY_SCRIPTS dtoverlay-pre dtoverlay-post) +foreach(_script ${DTOVERLAY_SCRIPTS}) + add_custom_command( + TARGET dtoverlay + COMMAND ${CMAKE_COMMAND} + -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${_script} + ${CMAKE_BINARY_DIR}/../../bin/${_script} + ) +endforeach() +install(PROGRAMS ${DTOVERLAY_SCRIPTS} DESTINATION bin) diff --git a/host_applications/linux/apps/dtoverlay/dtoverlay-post b/host_applications/linux/apps/dtoverlay/dtoverlay-post new file mode 100755 index 0000000..6d8c66d --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/dtoverlay-post @@ -0,0 +1,10 @@ +#!/bin/bash +if [ "$DISPLAY" == "" ]; then + export DISPLAY=":0.0" +fi +CMD="which lxpanelctl >/dev/null 2>&1 && lxpanelctl alsastart >/dev/null" +if [ $EUID -eq 0 ]; then + exec su pi -c "$CMD" +else + exec $CMD +fi diff --git a/host_applications/linux/apps/dtoverlay/dtoverlay-pre b/host_applications/linux/apps/dtoverlay/dtoverlay-pre new file mode 100755 index 0000000..5065fc5 --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/dtoverlay-pre @@ -0,0 +1,10 @@ +#!/bin/bash +if [ "$DISPLAY" == "" ]; then + export DISPLAY=":0.0" +fi +CMD="which lxpanelctl >/dev/null 2>&1 && lxpanelctl alsastop >/dev/null" +if [ $EUID -eq 0 ]; then + exec su pi -c "$CMD" +else + exec $CMD +fi diff --git a/host_applications/linux/apps/dtoverlay/dtoverlay_main.c b/host_applications/linux/apps/dtoverlay/dtoverlay_main.c new file mode 100755 index 0000000..5f87e66 --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/dtoverlay_main.c @@ -0,0 +1,1073 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dtoverlay.h" +#include "utils.h" + + +#define CFG_DIR_1 "/sys/kernel/config" +#define CFG_DIR_2 "/config" +#define DT_SUBDIR "/device-tree" +#define WORK_DIR "/tmp/.dtoverlays" +#define OVERLAY_SRC_SUBDIR "overlays" +#define README_FILE "README" +#define DT_OVERLAYS_SUBDIR "overlays" +#define DTOVERLAY_PATH_MAX 128 +#define DIR_MODE 0755 + + +enum { + OPT_ADD, + OPT_REMOVE, + OPT_REMOVE_FROM, + OPT_LIST, + OPT_LIST_ALL, + OPT_HELP +}; + +static const char *boot_dirs[] = +{ +#ifdef FORCE_BOOT_DIR + FORCE_BOOT_DIR, +#else + "/boot", + "/flash", +#ifdef OTHER_BOOT_DIR + OTHER_BOOT_DIR, +#endif +#endif + NULL /* Terminator */ +}; + +typedef struct state_struct +{ + int count; + struct dirent **namelist; +} STATE_T; + +static int dtoverlay_add(STATE_T *state, const char *overlay, + int argc, const char **argv); +static int dtoverlay_remove(STATE_T *state, const char *overlay, int and_later); +static int dtoverlay_list(STATE_T *state); +static int dtoverlay_list_all(STATE_T *state); +static void usage(void); +static void root_check(void); + +static void overlay_help(const char *overlay, const char **params); + +static int apply_overlay(const char *overlay_file, const char *overlay); +static int overlay_applied(const char *overlay_dir); + +static STATE_T *read_state(const char *dir); +static void free_state(STATE_T *state); + +const char *cmd_name; + +const char *work_dir = WORK_DIR; +const char *overlay_src_dir; +const char *dt_overlays_dir; +const char *error_file = NULL; +int dry_run = 0; + +int main(int argc, const char **argv) +{ + int argn = 1; + int opt = OPT_ADD; + int is_dtparam; + const char *overlay = NULL; + const char **params = NULL; + int ret = 0; + STATE_T *state = NULL; + const char *cfg_dir; + + cmd_name = argv[0]; + if (strrchr(cmd_name, '/')) + cmd_name = strrchr(cmd_name, '/') + 1; + is_dtparam = (strcmp(cmd_name, "dtparam") == 0); + + while ((argn < argc) && (argv[argn][0] == '-')) + { + const char *arg = argv[argn++]; + if (strcmp(arg, "-r") == 0) + { + if (opt != OPT_ADD) + usage(); + opt = OPT_REMOVE; + } + else if (strcmp(arg, "-R") == 0) + { + if (opt != OPT_ADD) + usage(); + opt = OPT_REMOVE_FROM; + } + else if (strcmp(arg, "-D") == 0) + { + if (opt != OPT_ADD) + usage(); + dry_run = 1; + work_dir = "."; + } + else if ((strcmp(arg, "-l") == 0) || + (strcmp(arg, "--list") == 0)) + { + if (opt != OPT_ADD) + usage(); + opt = OPT_LIST; + } + else if ((strcmp(arg, "-a") == 0) || + (strcmp(arg, "--listall") == 0) || + (strcmp(arg, "--all") == 0)) + { + if (opt != OPT_ADD) + usage(); + opt = OPT_LIST_ALL; + } + else if (strcmp(arg, "-d") == 0) + { + if (argn == argc) + usage(); + overlay_src_dir = argv[argn++]; + } + else if (strcmp(arg, "-v") == 0) + { + opt_verbose = 1; + } + else if (strcmp(arg, "-h") == 0) + { + opt = OPT_HELP; + } + else + { + fprintf(stderr, "* unknown option '%s'\n", arg); + usage(); + } + } + + if ((opt == OPT_ADD) || (opt == OPT_REMOVE) || + (opt == OPT_REMOVE_FROM) || (opt == OPT_HELP)) + { + if ((argn == argc) && + ((!is_dtparam && + ((opt == OPT_ADD) || (opt == OPT_HELP))) || + (is_dtparam && (opt == OPT_HELP)))) + usage(); + if (is_dtparam && (opt == OPT_ADD) && (argn == argc)) + opt = OPT_HELP; + if (is_dtparam && + ((opt == OPT_ADD) || (opt == OPT_HELP))) + overlay = "dtparam"; + else if (argn < argc) + overlay = argv[argn++]; + } + + if ((opt == OPT_HELP) && (argn < argc)) + { + params = &argv[argn]; + argn = argc; + } + + if ((opt != OPT_ADD) && (argn != argc)) + usage(); + + dtoverlay_enable_debug(opt_verbose); + + if (!overlay_src_dir) + { + /* Find the overlays and README */ + int i; + + for (i = 0; boot_dirs[i]; i++) + { + overlay_src_dir = sprintf_dup("%s/" OVERLAY_SRC_SUBDIR, + boot_dirs[i]); + if (dir_exists(overlay_src_dir)) + break; + free_string(overlay_src_dir); + overlay_src_dir = NULL; + } + + if (!overlay_src_dir) + fatal_error("Failed to find overlays directory"); + } + + if (opt == OPT_HELP) + { + overlay_help(overlay, params); + goto orderly_exit; + } + + if (!dir_exists(work_dir)) + { + if (mkdir(work_dir, DIR_MODE) != 0) + fatal_error("Failed to create '%s' - %d", work_dir, errno); + } + + error_file = sprintf_dup("%s/%s", work_dir, "error.dtb"); + + cfg_dir = CFG_DIR_1 DT_SUBDIR; + if (!dry_run && !dir_exists(cfg_dir)) + { + root_check(); + + cfg_dir = CFG_DIR_2; + if (!dir_exists(cfg_dir)) + { + if (mkdir(cfg_dir, DIR_MODE) != 0) + fatal_error("Failed to create '%s' - %d", cfg_dir, errno); + } + + cfg_dir = CFG_DIR_2 DT_SUBDIR; + if (!dir_exists(cfg_dir) && + (run_cmd("mount -t configfs none '%s'", cfg_dir) != 0)) + fatal_error("Failed to mount configfs - %d", errno); + } + + dt_overlays_dir = sprintf_dup("%s/%s", cfg_dir, DT_OVERLAYS_SUBDIR); + if (!dir_exists(dt_overlays_dir)) + fatal_error("configfs overlays folder not found - incompatible kernel"); + + if (!dry_run) + { + state = read_state(work_dir); + if (!state) + fatal_error("Failed to read state"); + } + + switch (opt) + { + case OPT_ADD: + case OPT_REMOVE: + case OPT_REMOVE_FROM: + if (!dry_run) + { + root_check(); + run_cmd("which dtoverlay-pre >/dev/null 2>&1 && dtoverlay-pre"); + } + break; + default: + break; + } + + switch (opt) + { + case OPT_ADD: + ret = dtoverlay_add(state, overlay, argc - argn, argv + argn); + break; + case OPT_REMOVE: + ret = dtoverlay_remove(state, overlay, 0); + break; + case OPT_REMOVE_FROM: + ret = dtoverlay_remove(state, overlay, 1); + break; + case OPT_LIST: + ret = dtoverlay_list(state); + break; + case OPT_LIST_ALL: + ret = dtoverlay_list_all(state); + break; + default: + ret = 1; + break; + } + + switch (opt) + { + case OPT_ADD: + case OPT_REMOVE: + case OPT_REMOVE_FROM: + if (!dry_run) + run_cmd("which dtoverlay-post >/dev/null 2>&1 && dtoverlay-post"); + break; + default: + break; + } + +orderly_exit: + if (state) + free_state(state); + free_strings(); + + if ((ret == 0) && error_file) + unlink(error_file); + + return ret; +} + +struct dtparam_state +{ + STRING_VEC_T *used_props; + const char *override_value; +}; + +int dtparam_callback(int override_type, + DTBLOB_T *dtb, int node_off, + const char *prop_name, int target_phandle, + int target_off, int target_size, + void *callback_value) +{ + struct dtparam_state *state = callback_value; + char prop_id[80]; + int err; + + err = dtoverlay_override_one_target(override_type, + dtb, node_off, + prop_name, target_phandle, + target_off, target_size, + (void *)state->override_value); + + if ((err == 0) && (target_phandle != 0)) + { + if (snprintf(prop_id, sizeof(prop_id), "%08x%s", target_phandle, + prop_name) < 0) + err = FDT_ERR_INTERNAL; + else if (string_vec_find(state->used_props, prop_id, 0) < 0) + string_vec_add(state->used_props, prop_id, 0); + } + + return err; +} + +// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors +int dtparam_apply(DTBLOB_T *dtb, const char *override_name, + const char *override_data, int data_len, + const char *override_value, STRING_VEC_T *used_props) +{ + struct dtparam_state state; + void *data; + int err; + + state.used_props = used_props; + state.override_value = override_value; + + /* Copy the override data in case it moves */ + data = malloc(data_len); + if (data) + { + memcpy(data, override_data, data_len); + err = dtoverlay_foreach_override_target(dtb, override_name, + data, data_len, + dtparam_callback, + (void *)&state); + free(data); + } + else + { + dtoverlay_error("out of memory"); + err = NON_FATAL(FDT_ERR_NOSPACE); + } + + return err; +} + +static int dtoverlay_add(STATE_T *state, const char *overlay, + int argc, const char **argv) +{ + const char *overlay_name; + const char *overlay_file; + char *param_string = NULL; + int is_dtparam; + DTBLOB_T *base_dtb = NULL; + DTBLOB_T *overlay_dtb; + STRING_VEC_T used_props; + int err; + int len; + int i; + + len = strlen(overlay) - 5; + is_dtparam = (strcmp(overlay, "dtparam") == 0); + if (is_dtparam) + { + /* Convert /proc/device-tree to a .dtb and load it */ + overlay_file = sprintf_dup("%s/%s", work_dir, "base.dtb"); + if (run_cmd("dtc -I fs -O dtb -o '%s' /proc/device-tree 1>/dev/null 2>&1", + overlay_file) != 0) + return error("Failed to read active DTB"); + } + else if ((len > 0) && (strcmp(overlay + len, ".dtbo") == 0)) + { + const char *p; + overlay_file = overlay; + p = strrchr(overlay, '/'); + if (p) + { + overlay = p + 1; + len = strlen(overlay) - 5; + } + + overlay = sprintf_dup("%.*s", len, overlay); + } + else + { + overlay_file = sprintf_dup("%s/%s.dtbo", overlay_src_dir, overlay); + } + + if (dry_run) + overlay_name = "dry_run"; + else + overlay_name = sprintf_dup("%d_%s", state->count, overlay); + overlay_dtb = dtoverlay_load_dtb(overlay_file, DTOVERLAY_PADDING(4096)); + if (!overlay_dtb) + return error("Failed to read '%s'", overlay_file); + + if (is_dtparam) + { + base_dtb = overlay_dtb; + string_vec_init(&used_props); + } + + /* Apply any parameters next */ + for (i = 0; i < argc; i++) + { + const char *arg = argv[i]; + const char *param_val = strchr(arg, '='); + const char *param, *override; + char *p = NULL; + int override_len; + if (param_val) + { + int len = (param_val - arg); + p = sprintf_dup("%.*s", len, arg); + param = p; + param_val++; + } + else + { + /* Use the default parameter value - true */ + param = arg; + param_val = "true"; + } + + override = dtoverlay_find_override(overlay_dtb, param, &override_len); + + if (!override) + return error("Unknown parameter '%s'", param); + + if (is_dtparam) + err = dtparam_apply(overlay_dtb, param, + override, override_len, + param_val, &used_props); + else + err = dtoverlay_apply_override(overlay_dtb, param, + override, override_len, + param_val); + if (err != 0) + return error("Failed to set %s=%s", param, param_val); + + param_string = sprintf_dup("%s %s=%s", + param_string ? param_string : "", + param, param_val); + + free_string(p); + } + + if (is_dtparam) + { + /* Build an overlay DTB */ + overlay_dtb = dtoverlay_create_dtb(2048 + 256 * used_props.num_strings); + + for (i = 0; i < used_props.num_strings; i++) + { + int phandle, node_off, prop_len; + const char *str, *prop_name; + const void *prop_data; + + str = used_props.strings[i]; + sscanf(str, "%8x", &phandle); + prop_name = str + 8; + node_off = dtoverlay_find_phandle(base_dtb, phandle); + + prop_data = dtoverlay_get_property(base_dtb, node_off, + prop_name, &prop_len); + err = dtoverlay_create_prop_fragment(overlay_dtb, i, phandle, + prop_name, prop_data, prop_len); + } + + dtoverlay_free_dtb(base_dtb); + } + + if (param_string) + dtoverlay_dtb_set_trailer(overlay_dtb, param_string, + strlen(param_string) + 1); + + /* Create a filename with the sequence number */ + overlay_file = sprintf_dup("%s/%s.dtbo", work_dir, overlay_name); + + /* then write the overlay to the file */ + dtoverlay_pack_dtb(overlay_dtb); + dtoverlay_save_dtb(overlay_dtb, overlay_file); + dtoverlay_free_dtb(overlay_dtb); + + if (!dry_run && !apply_overlay(overlay_file, overlay_name)) + { + if (error_file) + { + rename(overlay_file, error_file); + free_string(error_file); + } + return 1; + } + + return 0; +} + +static int dtoverlay_remove(STATE_T *state, const char *overlay, int and_later) +{ + const char *overlay_dir; + const char *dir_name = NULL; + char *end; + int overlay_len; + int count = state->count; + int rmpos; + int i; + + if (chdir(work_dir) != 0) + fatal_error("Failed to chdir to '%s'", work_dir); + + if (overlay) + { + overlay_len = strlen(overlay); + + rmpos = strtoul(overlay, &end, 10); + if (end && (*end == '\0')) + { + if (rmpos >= count) + return error("Overlay index (%d) too large", rmpos); + dir_name = state->namelist[rmpos]->d_name; + } + /* Locate the most recent reference to the overlay */ + else for (rmpos = count - 1; rmpos >= 0; rmpos--) + { + const char *left, *right; + dir_name = state->namelist[rmpos]->d_name; + left = strchr(dir_name, '_'); + if (!left) + return error("Internal error"); + left++; + right = strchr(left, '.'); + if (!right) + return error("Internal error"); + if (((right - left) == overlay_len) && + (memcmp(overlay, left, overlay_len) == 0)) + break; + dir_name = NULL; + } + + if (rmpos < 0) + return error("Overlay '%s' is not loaded", overlay); + } + else + { + if (!count) + return error("No overlays loaded"); + rmpos = and_later ? 0 : (count - 1); + dir_name = state->namelist[rmpos]->d_name; + } + + if (rmpos < count) + { + /* Unload it and all subsequent overlays in reverse order */ + for (i = count - 1; i >= rmpos; i--) + { + const char *left, *right; + left = state->namelist[i]->d_name; + right = strrchr(left, '.'); + if (!right) + return error("Internal error"); + + overlay_dir = sprintf_dup("%s/%.*s", dt_overlays_dir, + right - left, left); + if (rmdir(overlay_dir) != 0) + return error("Failed to remove directory '%s'", overlay_dir); + + free_string(overlay_dir); + } + + /* Replay the sequence, deleting files for the specified overlay, + and renumbering and reloading all other overlays. */ + for (i = rmpos, state->count = rmpos; i < count; i++) + { + const char *left, *right; + const char *filename = state->namelist[i]->d_name; + + left = strchr(filename, '_'); + if (!left) + return error("Internal error"); + left++; + right = strchr(left, '.'); + if (!right) + return error("Internal error"); + + if (and_later || (i == rmpos)) + { + /* This one is being deleted */ + unlink(filename); + } + else + { + /* Keep this one - renumber and reload */ + int len = right - left; + char *new_name = sprintf_dup("%d_%.*s", state->count, + len, left); + char *new_file = sprintf_dup("%s.dtbo", new_name); + int ret = 0; + + if ((len == 7) && (memcmp(left, "dtparam", 7) == 0)) + { + /* Regenerate the overlay in case multiple overlays target + different parts of the same property. */ + + DTBLOB_T *dtb; + char *params; + const char **paramv; + int paramc; + int j; + char *p; + + /* Extract the parameters */ + dtb = dtoverlay_load_dtb(filename, 0); + unlink(filename); + + if (!dtb) + { + error("Failed to re-apply dtparam"); + continue; + } + + params = (char *)dtoverlay_dtb_trailer(dtb); + if (!params) + { + error("Failed to re-apply dtparam"); + dtoverlay_free_dtb(dtb); + continue; + } + + /* Count and NUL-separate the params */ + p = params; + paramc = 0; + while (*p) + { + int paramlen; + *(p++) = '\0'; + paramlen = strcspn(p, " "); + paramc++; + p += paramlen; + } + + paramv = malloc((paramc + 1) * sizeof(const char *)); + if (!paramv) + { + error("out of memory re-applying dtparam"); + dtoverlay_free_dtb(dtb); + continue; + } + + for (j = 0, p = params + 1; j < paramc; j++) + { + paramv[j] = p; + p += strlen(p) + 1; + } + paramv[j] = NULL; + + /* Create the new overlay */ + ret = dtoverlay_add(state, "dtparam", paramc, paramv); + free(paramv); + dtoverlay_free_dtb(dtb); + } + else + { + rename(filename, new_file); + ret = !apply_overlay(new_file, new_name); + } + if (ret != 0) + { + error("Failed to re-apply dtparam"); + continue; + } + state->count++; + } + } + } + + return 0; +} + +static int dtoverlay_list(STATE_T *state) +{ + if (state->count == 0) + { + printf("No overlays loaded\n"); + } + else + { + int i; + printf("Overlays (in load order):\n"); + for (i = 0; i < state->count; i++) + { + const char *name, *left, *right; + const char *saved_overlay; + DTBLOB_T *dtb; + name = state->namelist[i]->d_name; + left = strchr(name, '_'); + if (!left) + return error("Internal error"); + left++; + right = strchr(left, '.'); + if (!right) + return error("Internal error"); + + saved_overlay = sprintf_dup("%s/%s", work_dir, name); + dtb = dtoverlay_load_dtb(saved_overlay, 0); + + if (dtoverlay_dtb_trailer(dtb)) + printf("%d: %.*s %.*s\n", i, (int)(right - left), left, + dtoverlay_dtb_trailer_len(dtb), + (char *)dtoverlay_dtb_trailer(dtb)); + else + printf("%d: %.*s\n", i, (int)(right - left), left); + + dtoverlay_free_dtb(dtb); + } + } + + return 0; +} + +static int dtoverlay_list_all(STATE_T *state) +{ + int i; + DIR *dh; + struct dirent *de; + STRING_VEC_T strings; + + string_vec_init(&strings); + + /* Enumerate .dtbo files in the /boot/overlays directory */ + dh = opendir(overlay_src_dir); + while ((de = readdir(dh)) != NULL) + { + int len = strlen(de->d_name) - 5; + if ((len >= 0) && strcmp(de->d_name + len, ".dtbo") == 0) + { + char *str = string_vec_add(&strings, de->d_name, len + 2); + str[len] = '\0'; + str[len + 1] = ' '; + } + } + closedir(dh); + + /* Merge in active overlays, marking them */ + for (i = 0; i < state->count; i++) + { + const char *left, *right; + char *str; + int len, idx; + + left = strchr(state->namelist[i]->d_name, '_'); + if (!left) + return error("Internal error"); + left++; + right = strchr(left, '.'); + if (!right) + return error("Internal error"); + + len = right - left; + if ((len == 7) && (memcmp(left, "dtparam", 7) == 0)) + continue; + idx = string_vec_find(&strings, left, len); + if (idx >= 0) + { + str = strings.strings[idx]; + len = strlen(str); + } + else + { + str = string_vec_add(&strings, left, len + 2); + str[len] = '\0'; + } + str[len + 1] = '*'; + } + + if (strings.num_strings == 0) + { + printf("No overlays found\n"); + } + else + { + /* Sort */ + string_vec_sort(&strings); + + /* Display */ + printf("All overlays (* = loaded):\n"); + + for (i = 0; i < strings.num_strings; i++) + { + const char *str = strings.strings[i]; + printf("%c %s\n", str[strlen(str)+1], str); + } + } + + string_vec_uninit(&strings); + + return 0; +} + +static void usage(void) +{ + printf("Usage:\n"); + if (strcmp(cmd_name, "dtparam") == 0) + { + printf(" %s Display help on all parameters\n", cmd_name); + printf(" %s =...\n", cmd_name); + printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), ""); + printf(" %s -D [] Dry-run (prepare overlay, but don't apply -\n", + cmd_name); + printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), ""); + printf(" %s -r [] Remove an overlay (by index, or the last)\n", cmd_name); + printf(" %s -R [] Remove from an overlay (by index, or all)\n", + cmd_name); + printf(" %s -l List active overlays/dtparams\n", cmd_name); + printf(" %s -a List all overlays/dtparams (marking the active)\n", cmd_name); + printf(" %s -h Show this usage message\n", cmd_name); + printf(" %s -h ... Display help on the listed parameters\n", cmd_name); + } + else + { + printf(" %s [=...]\n", cmd_name); + printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), ""); + printf(" %s -D [] Dry-run (prepare overlay, but don't apply -\n", + cmd_name); + printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), ""); + printf(" %s -r [] Remove an overlay (by name, index or the last)\n", cmd_name); + printf(" %s -R [] Remove from an overlay (by name, index or all)\n", + cmd_name); + printf(" %s -l List active overlays/params\n", cmd_name); + printf(" %s -a List all overlays (marking the active)\n", cmd_name); + printf(" %s -h Show this usage message\n", cmd_name); + printf(" %s -h Display help on an overlay\n", cmd_name); + printf(" %s -h .. Or its parameters\n", cmd_name); + printf(" where is the name of an overlay or 'dtparam' for dtparams\n"); + } + printf("Options applicable to most variants:\n"); + printf(" -d Specify an alternate location for the overlays\n"); + printf(" (defaults to /boot/overlays or /flash/overlays)\n"); + printf(" -v Verbose operation\n"); + printf("\n"); + printf("Adding or removing overlays and parameters requires root privileges.\n"); + + exit(1); +} + +static void root_check(void) +{ + if (getuid() != 0) + fatal_error("Must be run as root - try 'sudo %s ...'", cmd_name); +} + +static void overlay_help(const char *overlay, const char **params) +{ + OVERLAY_HELP_STATE_T *state; + const char *readme_path = sprintf_dup("%s/%s", overlay_src_dir, + README_FILE); + + state = overlay_help_open(readme_path); + free_string(readme_path); + + if (state) + { + if (strcmp(overlay, "dtparam") == 0) + overlay = ""; + + if (overlay_help_find(state, overlay)) + { + if (params && overlay_help_find_field(state, "Params")) + { + int in_param = 0; + + while (1) + { + const char *line = overlay_help_field_data(state); + if (!line) + break; + if (line[0] == '\0') + continue; + if (line[0] != ' ') + { + /* This is a parameter name */ + int param_len = strcspn(line, " "); + const char **p = params; + in_param = 0; + while (*p) + { + if ((param_len == strlen(*p)) && + (memcmp(line, *p, param_len) == 0)) + { + in_param = 1; + break; + } + p++; + } + } + if (in_param) + printf("%s\n", line); + } + } + else + { + printf("Name: %s\n\n", overlay); + overlay_help_print_field(state, "Info", "Info:", 8, 0); + overlay_help_print_field(state, "Load", "Usage:", 8, 0); + overlay_help_print_field(state, "Params", "Params:", 8, 0); + } + } + else + { + fatal_error("No help found for overlay '%s'", overlay); + } + overlay_help_close(state); + } + else + { + fatal_error("Help file not found"); + } +} + +static int apply_overlay(const char *overlay_file, const char *overlay) +{ + const char *overlay_dir = sprintf_dup("%s/%s", dt_overlays_dir, overlay); + int ret = 0; + if (dir_exists(overlay_dir)) + { + error("Overlay '%s' is already loaded", overlay); + } + else if (mkdir(overlay_dir, DIR_MODE) == 0) + { + DTBLOB_T *dtb = dtoverlay_load_dtb(overlay_file, 0); + if (!dtb) + { + error("Failed to apply overlay '%s' (load)", overlay); + } + else + { + const char *dest_file = sprintf_dup("%s/dtbo", overlay_dir); + + /* then write the overlay to the file */ + if (dtoverlay_save_dtb(dtb, dest_file) != 0) + error("Failed to apply overlay '%s' (save)", overlay); + else if (!overlay_applied(overlay_dir)) + error("Failed to apply overlay '%s' (kernel)", overlay); + else + ret = 1; + + free_string(dest_file); + dtoverlay_free_dtb(dtb); + } + + if (!ret) + rmdir(overlay_dir); + } + else + { + error("Failed to create overlay directory"); + } + + return ret; +} + +static int overlay_applied(const char *overlay_dir) +{ + char status[7] = { '\0' }; + const char *status_path = sprintf_dup("%s/status", overlay_dir); + FILE *fp = fopen(status_path, "r"); + int bytes = 0; + if (fp) + { + bytes = fread(status, 1, sizeof(status), fp); + fclose(fp); + } + free_string(status_path); + return (bytes == sizeof(status)) && + (memcmp(status, "applied", sizeof(status)) == 0); +} + +int seq_filter(const struct dirent *de) +{ + int num; + return (sscanf(de->d_name, "%d_", &num) == 1); +} + +int seq_compare(const struct dirent **de1, const struct dirent **de2) +{ + int num1 = atoi((*de1)->d_name); + int num2 = atoi((*de2)->d_name); + if (num1 < num2) + return -1; + else if (num1 == num2) + return 0; + else + return 1; +} + +static STATE_T *read_state(const char *dir) +{ + STATE_T *state = malloc(sizeof(STATE_T)); + int i; + + if (state) + { + state->count = scandir(dir, &state->namelist, seq_filter, seq_compare); + + for (i = 0; i < state->count; i++) + { + int num = atoi(state->namelist[i]->d_name); + if (i != num) + error("Overlay sequence error"); + } + } + return state; +} + +static void free_state(STATE_T *state) +{ + int i; + for (i = 0; i < state->count; i++) + { + free(state->namelist[i]); + } + free(state->namelist); + free(state); +} diff --git a/host_applications/linux/apps/dtoverlay/utils.c b/host_applications/linux/apps/dtoverlay/utils.c new file mode 100755 index 0000000..511efd9 --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/utils.c @@ -0,0 +1,463 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +#define OVERLAY_HELP_INDENT 8 + +int opt_verbose; +int opt_dry_run; +static STRING_T *allocated_strings; + +struct overlay_help_state_struct +{ + FILE *fp; + long rec_pos; + int line_len; + int line_pos; + int blank_count; + int end_of_field; + char line_buf[82]; +}; + +static int overlay_help_get_line(OVERLAY_HELP_STATE_T *state); + +OVERLAY_HELP_STATE_T *overlay_help_open(const char *helpfile) +{ + OVERLAY_HELP_STATE_T *state = NULL; + FILE *fp = fopen(helpfile, "r"); + if (fp) + { + state = calloc(1, sizeof(OVERLAY_HELP_STATE_T)); + if (!state) + fatal_error("Out of memory"); + state->fp = fp; + state->line_pos = -1; + state->rec_pos = -1; + } + + return state; +} + +void overlay_help_close(OVERLAY_HELP_STATE_T *state) +{ + fclose(state->fp); + free(state); +} + +int overlay_help_find(OVERLAY_HELP_STATE_T *state, const char *name) +{ + state->line_pos = -1; + state->rec_pos = -1; + state->blank_count = 0; + + fseek(state->fp, 0, SEEK_SET); + + while (overlay_help_find_field(state, "Name")) + { + const char *overlay = overlay_help_field_data(state); + if (overlay && (strcmp(overlay, name) == 0)) + { + state->rec_pos = (long)ftell(state->fp); + return 1; + } + } + + return 0; +} + +int overlay_help_find_field(OVERLAY_HELP_STATE_T *state, const char *field) +{ + int field_len = strlen(field); + int found = 0; + + if (state->rec_pos >= 0) + fseek(state->fp, state->rec_pos, SEEK_SET); + + while (!found) + { + int line_len = overlay_help_get_line(state); + if (line_len < 0) + break; + + /* Check for the ":" prefix */ + if ((line_len >= (field_len + 1)) && + (state->line_buf[field_len] == ':') && + (memcmp(state->line_buf, field, field_len) == 0)) + { + /* Found it + If this initial line has no content then skip it */ + if (line_len > OVERLAY_HELP_INDENT) + state->line_pos = OVERLAY_HELP_INDENT; + else + state->line_pos = -1; + state->end_of_field = 0; + found = 1; + } + else + { + state->line_pos = -1; + } + } + + return found; +} + +const char *overlay_help_field_data(OVERLAY_HELP_STATE_T *state) +{ + int line_len, pos; + + if (state->end_of_field) + return NULL; + + line_len = state->line_len; + + if ((state->line_pos < 0) || + (state->line_pos >= line_len)) + { + line_len = overlay_help_get_line(state); + + /* Fields end at the start of the next field or the end of the record */ + if ((line_len < 0) || (state->line_buf[0] != ' ')) + { + state->end_of_field = 1; + return NULL; + } + + if (line_len == 0) + return ""; + } + + /* Return field data starting at OVERLAY_HELP_INDENT, if there is any */ + pos = line_len; + if (pos > OVERLAY_HELP_INDENT) + pos = OVERLAY_HELP_INDENT; + + state->line_pos = -1; + return &state->line_buf[pos]; +} + +void overlay_help_print_field(OVERLAY_HELP_STATE_T *state, + const char *field, const char *label, + int indent, int strip_blanks) +{ + if (!overlay_help_find_field(state, field)) + return; + + while (1) + { + const char *line = overlay_help_field_data(state); + if (!line) + break; + + if (label) + { + int spaces = indent - strlen(label); + if (spaces < 0) + spaces = 0; + + printf("%s%*s%s\n", label, spaces, "", line); + label = NULL; + } + else if (line[0]) + { + printf("%*s%s\n", indent, "", line); + } + else if (!strip_blanks) + { + printf("\n"); + } + } + + if (!strip_blanks) + printf("\n"); +} + +/* Returns the length of the line, or -1 on end of file or record */ +static int overlay_help_get_line(OVERLAY_HELP_STATE_T *state) +{ + int line_len; + + if (state->line_pos >= 0) + return state->line_len; + +get_next_line: + state->line_buf[sizeof(state->line_buf) - 1] = ' '; + line_len = -1; + if (fgets(state->line_buf, sizeof(state->line_buf), state->fp)) + { + // Check for overflow + + // Strip the newline + line_len = strlen(state->line_buf); + if (line_len && (state->line_buf[line_len - 1] == '\n')) + { + line_len--; + state->line_buf[line_len] = '\0'; + } + } + + if (state->rec_pos >= 0) + { + if (line_len == 0) + { + state->blank_count++; + if (state->blank_count >= 2) + return -1; + state->line_pos = 0; + goto get_next_line; + } + else if (state->blank_count) + { + /* Return a single blank line now - the non-empty line will be + returned next time */ + state->blank_count = 0; + return 0; + } + } + + state->line_len = line_len; + state->line_pos = (line_len >= 0) ? 0 : -1; + return line_len; +} + +int run_cmd(const char *fmt, ...) +{ + va_list ap; + char *cmd; + int ret; + va_start(ap, fmt); + cmd = vsprintf_dup(fmt, ap); + va_end(ap); + + if (opt_dry_run || opt_verbose) + fprintf(stderr, "run_cmd: %s\n", cmd); + + ret = opt_dry_run ? 0 : system(cmd); + + free_string(cmd); + + return ret; +} + + +/* Not thread safe */ +void free_string(const char *string) +{ + STRING_T *str; + + if (!string) + return; + + str = (STRING_T *)(string - sizeof(STRING_T)); + if (str == allocated_strings) + { + allocated_strings = str->next; + if (allocated_strings == str) + allocated_strings = NULL; + } + str->prev->next = str->next; + str->next->prev = str->prev; + free(str); +} + +/* Not thread safe */ +void free_strings(void) +{ + if (allocated_strings) + { + STRING_T *str = allocated_strings; + do + { + STRING_T *t = str; + str = t->next; + free(t); + } while (str != allocated_strings); + allocated_strings = NULL; + } +} + +/* Not thread safe */ +char *sprintf_dup(const char *fmt, ...) +{ + va_list ap; + char *str; + va_start(ap, fmt); + str = vsprintf_dup(fmt, ap); + va_end(ap); + return str; +} + +/* Not thread safe */ +char *vsprintf_dup(const char *fmt, va_list ap) +{ + char scratch[512]; + int len; + STRING_T *str; + len = vsnprintf(scratch, sizeof(scratch), fmt, ap) + 1; + + if (len > sizeof(scratch)) + fatal_error("Maximum string length exceeded"); + + str = malloc(sizeof(STRING_T) + len); + if (!str) + fatal_error("Out of memory"); + + memcpy(str->data, scratch, len); + if (allocated_strings) + { + str->next = allocated_strings; + str->prev = allocated_strings->prev; + str->next->prev = str; + str->prev->next = str; + } + else + { + str->next = str; + str->prev = str; + allocated_strings = str; + } + + return str->data; +} + +int dir_exists(const char *dirname) +{ + struct stat finfo; + return (stat(dirname, &finfo) == 0) && S_ISDIR(finfo.st_mode); +} + +int file_exists(const char *dirname) +{ + struct stat finfo; + return (stat(dirname, &finfo) == 0) && S_ISREG(finfo.st_mode); +} + +void string_vec_init(STRING_VEC_T *vec) +{ + vec->num_strings = 0; + vec->max_strings = 0; + vec->strings = NULL; +} + +char *string_vec_add(STRING_VEC_T *vec, const char *str, int len) +{ + char *copy; + if (vec->num_strings == vec->max_strings) + { + if (vec->max_strings) + vec->max_strings *= 2; + else + vec->max_strings = 16; + vec->strings = realloc(vec->strings, vec->max_strings * sizeof(const char *)); + if (!vec->strings) + fatal_error("Out of memory"); + } + + if (len) + { + copy = malloc(len + 1); + strncpy(copy, str, len); + copy[len] = '\0'; + } + else + copy = strdup(str); + + if (!copy) + fatal_error("Out of memory"); + + vec->strings[vec->num_strings++] = copy; + + return copy; +} + +int string_vec_find(STRING_VEC_T *vec, const char *str, int len) +{ + int i; + + for (i = 0; i < vec->num_strings; i++) + { + if (len) + { + if ((strncmp(vec->strings[i], str, len) == 0) && + (vec->strings[i][len] == '\0')) + return i; + } + else if (strcmp(vec->strings[i], str) == 0) + return i; + } + + return -1; +} + +int string_vec_compare(const void *a, const void *b) +{ + return strcmp(*(const char **)a, *(const char **)b); +} + +void string_vec_sort(STRING_VEC_T *vec) +{ + qsort(vec->strings, vec->num_strings, sizeof(char *), &string_vec_compare); +} + +void string_vec_uninit(STRING_VEC_T *vec) +{ + int i; + for (i = 0; i < vec->num_strings; i++) + free(vec->strings[i]); + free(vec->strings); +} + +int error(const char *fmt, ...) +{ + va_list ap; + fprintf(stderr, "* "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + return 1; +} + +void fatal_error(const char *fmt, ...) +{ + va_list ap; + fprintf(stderr, "* "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(1); +} diff --git a/host_applications/linux/apps/dtoverlay/utils.h b/host_applications/linux/apps/dtoverlay/utils.h new file mode 100755 index 0000000..3d3b59f --- /dev/null +++ b/host_applications/linux/apps/dtoverlay/utils.h @@ -0,0 +1,74 @@ +/* +Copyright (c) 2016 Raspberry Pi (Trading) Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTILS_H +#define UTILS_H + +typedef struct string_struct +{ + struct string_struct *prev; + struct string_struct *next; + char data[0]; +} STRING_T; + +typedef struct string_vec_struct +{ + int num_strings; + int max_strings; + char **strings; +} STRING_VEC_T; + +typedef struct overlay_help_state_struct OVERLAY_HELP_STATE_T; + +extern int opt_verbose; +extern int opt_dry_run; + +OVERLAY_HELP_STATE_T *overlay_help_open(const char *helpfile); +void overlay_help_close(OVERLAY_HELP_STATE_T *state); +int overlay_help_find(OVERLAY_HELP_STATE_T *state, const char *name); +int overlay_help_find_field(OVERLAY_HELP_STATE_T *state, const char *field); +const char *overlay_help_field_data(OVERLAY_HELP_STATE_T *state); +void overlay_help_print_field(OVERLAY_HELP_STATE_T *state, + const char *field, const char *label, + int indent, int strip_blanks); + +int run_cmd(const char *fmt, ...); +void free_string(const char *string); /* Not thread safe */ +void free_strings(void); /* Not thread safe */ +char *sprintf_dup(const char *fmt, ...); /* Not thread safe */ +char *vsprintf_dup(const char *fmt, va_list ap); /* Not thread safe */ +int dir_exists(const char *dirname); +int file_exists(const char *dirname); +void string_vec_init(STRING_VEC_T *vec); +char *string_vec_add(STRING_VEC_T *vec, const char *str, int len); +int string_vec_find(STRING_VEC_T *vec, const char *str, int len); +void string_vec_sort(STRING_VEC_T *vec); +void string_vec_uninit(STRING_VEC_T *vec); +int error(const char *fmt, ...); +void fatal_error(const char *fmt, ...); + +#endif diff --git a/host_applications/linux/apps/gencmd/CMakeLists.txt b/host_applications/linux/apps/gencmd/CMakeLists.txt new file mode 100755 index 0000000..f95d1a1 --- /dev/null +++ b/host_applications/linux/apps/gencmd/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.8) + +if (WIN32) + set(VCOS_PLATFORM win32) +else () + set(VCOS_PLATFORM pthreads) + add_definitions(-Wall -Werror) +endif () + +include_directories( ../../../.. + ../../../../interface/vcos + ../../../../interface/vcos/${VCOS_PLATFORM} ) + +#add_subdirectory( ../../../../interface/vcos/${VCOS_PLATFORM} vcos) +#add_subdirectory( ../../bin/gencmd) + +add_executable(vcgencmd gencmd.c) +target_link_libraries(vcgencmd vcos vchiq_arm vchostif) +install(TARGETS vcgencmd RUNTIME DESTINATION bin) + diff --git a/host_applications/linux/apps/gencmd/gencmd.c b/host_applications/linux/apps/gencmd/gencmd.c new file mode 100755 index 0000000..9b48cf9 --- /dev/null +++ b/host_applications/linux/apps/gencmd/gencmd.c @@ -0,0 +1,147 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/* ---- Include Files ---------------------------------------------------- */ + +#include +#include +#include +#include +#include + +#include "interface/vmcs_host/vc_vchi_gencmd.h" + +int main( int argc, char **argv ) +{ + int instNum = 0; + VCHI_INSTANCE_T vchi_instance; + VCHI_CONNECTION_T *vchi_connection = NULL; + + if ( argc > 1 ) + { + if (( strcmp( argv[1], "0" ) == 0 ) || ( strcmp( argv[1], "1" ) == 0 )) + { + instNum = atoi( argv[1] ); + argv++; + argc--; + } + } + + vcos_init(); + + if ( vchi_initialise( &vchi_instance ) != 0) + { + printf( "VCHI initialization failed\n" ); + return -1; + } + + //create a vchi connection + if ( vchi_connect( NULL, 0, vchi_instance ) != 0) + { + printf( "VCHI connection failed\n" ); + return -1; + } + + vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1 ); + + if (argc > 1) + { + int i = 1; + char buffer[ 1024 ]; + size_t buffer_offset = 0; + clock_t before=0, after=0; + double time_diff; + uint32_t show_time = 0; + int ret; + + //reset the string + buffer[0] = '\0'; + + //first, strip out a potential leading -t + if( strcmp( argv[1], "-t" ) == 0 ) + { + show_time = 1; + i++; + } + + for (; i <= argc-1; i++) + { + buffer_offset = vcos_safe_strcpy( buffer, argv[i], sizeof(buffer), buffer_offset ); + buffer_offset = vcos_safe_strcpy( buffer, " ", sizeof(buffer), buffer_offset ); + } + + if( show_time ) + before = clock(); + + //send the gencmd for the argument + if (( ret = vc_gencmd_send( "%s", buffer )) != 0 ) + { + printf( "vc_gencmd_send returned %d\n", ret ); + } + + //get + print out the response! + if (( ret = vc_gencmd_read_response( buffer, sizeof( buffer ) )) != 0 ) + { + printf( "vc_gencmd_read_response returned %d\n", ret ); + } + + if( show_time ) + after = clock(); + + if( show_time ) + { + time_diff = ((double) (after - before)) / CLOCKS_PER_SEC; + + printf( "Time took %f seconds (%f msecs) (%f usecs)\n", time_diff, time_diff * 1000, time_diff * 1000000 ); + } + + if ( buffer[0] != '\0' ) + { + if ( buffer[ strlen( buffer) - 1] == '\n' ) + { + fputs( buffer, stdout ); + } + else + { + printf("%s\n", buffer ); + } + } + } + + vc_gencmd_stop(); + + //close the vchi connection + if ( vchi_disconnect( vchi_instance ) != 0) + { + printf( "VCHI disconnect failed\n" ); + return -1; + } + + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/CMakeLists.txt b/host_applications/linux/apps/hello_pi/CMakeLists.txt new file mode 100755 index 0000000..21b29f3 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/CMakeLists.txt @@ -0,0 +1,31 @@ +set(BUILD_FONT FALSE) + +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/libs/bcm_host/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/ilclient) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/vgfont) + +set(ILCLIENT_SRCS libs/ilclient/ilclient.c libs/ilclient/ilcore.c) +add_library(ilclient ${ILCLIENT_SRCS}) + +set(HELLO_PI_LIBS ilclient openmaxil bcm_host vcos vchiq_arm) + +add_subdirectory(hello_world) +add_subdirectory(hello_video) +add_subdirectory(hello_audio) +#add_subdirectory(hello_triangle) +#add_subdirectory(hello_triangle2) +#add_subdirectory(hello_dispmanx) +#add_subdirectory(hello_tiger) +#add_subdirectory(hello_encode) +#add_subdirectory(hello_jpeg) +#add_subdirectory(hello_videocube) +#add_subdirectory(hello_teapot) + +if(BUILD_FONT) +set(VGFONT_SRCS libs/vgfont/font.c libs/vgfont/vgft.c libs/vgfont/graphics.c) +set_source_files_properties(${VGFONT_SRCS} PROPERTIES COMPILE_DEFINITIONS) +add_library(vgfont ${VGFONT_SRCS}) + +add_subdirectory(hello_font) +endif(BUILD_FONT) diff --git a/host_applications/linux/apps/hello_pi/Makefile.include b/host_applications/linux/apps/hello_pi/Makefile.include new file mode 100755 index 0000000..fecf555 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/Makefile.include @@ -0,0 +1,28 @@ + +CFLAGS+=-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -U_FORTIFY_SOURCE -Wall -g -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -Wno-psabi + +LDFLAGS+=-L$(SDKSTAGE)/opt/vc/lib/ -lbrcmGLESv2 -lbrcmEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm -L$(SDKSTAGE)/opt/vc/src/hello_pi/libs/ilclient -L$(SDKSTAGE)/opt/vc/src/hello_pi/libs/vgfont + +INCLUDES+=-I$(SDKSTAGE)/opt/vc/include/ -I$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads -I$(SDKSTAGE)/opt/vc/include/interface/vmcs_host/linux -I./ -I$(SDKSTAGE)/opt/vc/src/hello_pi/libs/ilclient -I$(SDKSTAGE)/opt/vc/src/hello_pi/libs/vgfont + +all: $(BIN) $(LIB) + +%.o: %.c + @rm -f $@ + $(CC) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations + +%.o: %.cpp + @rm -f $@ + $(CXX) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations + +%.bin: $(OBJS) + $(CC) -o $@ -Wl,--whole-archive $(OBJS) $(LDFLAGS) -Wl,--no-whole-archive -rdynamic + +%.a: $(OBJS) + $(AR) r $@ $^ + +clean: + for i in $(OBJS); do (if test -e "$$i"; then ( rm $$i ); fi ); done + @rm -f $(BIN) $(LIB) + + diff --git a/host_applications/linux/apps/hello_pi/README b/host_applications/linux/apps/hello_pi/README new file mode 100755 index 0000000..7a91127 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/README @@ -0,0 +1,21 @@ +Building on Pi +++++++++++++++ + +To build the test apps on the pi, first build the libs: +make -C libs/ilclient +make -C libs/vgfont + +then by entering each test app directory and run make. E.g. + cd hello_world + make + ./hello_world.bin + +Running ./rebuild.sh will rebuild the all libs and and apps. + + +Building on a different PC +++++++++++++++++++++++++++ + +If you want to build the samples on a different machine (cross-compile) then set: +SDKSTAGE= and CC= +before running make. diff --git a/host_applications/linux/apps/hello_pi/hello_audio/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_audio/CMakeLists.txt new file mode 100755 index 0000000..03207c5 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_audio/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_audio.bin) +set(SRCS audio.c sinewave.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_audio/Makefile b/host_applications/linux/apps/hello_pi/hello_audio/Makefile new file mode 100755 index 0000000..3c3c139 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_audio/Makefile @@ -0,0 +1,6 @@ +OBJS=audio.o sinewave.o +BIN=hello_audio.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_audio/audio.c b/host_applications/linux/apps/hello_pi/hello_audio/audio.c new file mode 100755 index 0000000..ebbcbce --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_audio/audio.c @@ -0,0 +1,426 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Audio output demo using OpenMAX IL though the ilcient helper library + +#include +#include +#include +#include +#include +#include + +#include "bcm_host.h" +#include "ilclient.h" + +#define N_WAVE 1024 /* dimension of Sinewave[] */ +#define PI (1<<16>>1) +#define SIN(x) Sinewave[((x)>>6) & (N_WAVE-1)] +#define COS(x) SIN((x)+(PI>>1)) +#define OUT_CHANNELS(num_channels) ((num_channels) > 4 ? 8: (num_channels) > 2 ? 4: (num_channels)) +extern short Sinewave[]; + +#ifndef countof + #define countof(arr) (sizeof(arr) / sizeof(arr[0])) +#endif + +#define BUFFER_SIZE_SAMPLES 1024 + +typedef int int32_t; + +typedef struct { + sem_t sema; + ILCLIENT_T *client; + COMPONENT_T *audio_render; + COMPONENT_T *list[2]; + OMX_BUFFERHEADERTYPE *user_buffer_list; // buffers owned by the client + uint32_t num_buffers; + uint32_t bytes_per_sample; +} AUDIOPLAY_STATE_T; + +static void input_buffer_callback(void *data, COMPONENT_T *comp) +{ + // do nothing - could add a callback to the user + // to indicate more buffers may be available. +} + +int32_t audioplay_create(AUDIOPLAY_STATE_T **handle, + uint32_t sample_rate, + uint32_t num_channels, + uint32_t bit_depth, + uint32_t num_buffers, + uint32_t buffer_size) +{ + uint32_t bytes_per_sample = (bit_depth * OUT_CHANNELS(num_channels)) >> 3; + int32_t ret = -1; + + *handle = NULL; + + // basic sanity check on arguments + if(sample_rate >= 8000 && sample_rate <= 192000 && + (num_channels >= 1 && num_channels <= 8) && + (bit_depth == 16 || bit_depth == 32) && + num_buffers > 0 && + buffer_size >= bytes_per_sample) + { + // buffer lengths must be 16 byte aligned for VCHI + int size = (buffer_size + 15) & ~15; + AUDIOPLAY_STATE_T *st; + + // buffer offsets must also be 16 byte aligned for VCHI + st = calloc(1, sizeof(AUDIOPLAY_STATE_T)); + + if(st) + { + OMX_ERRORTYPE error; + OMX_PARAM_PORTDEFINITIONTYPE param; + OMX_AUDIO_PARAM_PCMMODETYPE pcm; + int32_t s; + + ret = 0; + *handle = st; + + // create and start up everything + s = sem_init(&st->sema, 0, 1); + assert(s == 0); + + st->bytes_per_sample = bytes_per_sample; + st->num_buffers = num_buffers; + + st->client = ilclient_init(); + assert(st->client != NULL); + + ilclient_set_empty_buffer_done_callback(st->client, input_buffer_callback, st); + + error = OMX_Init(); + assert(error == OMX_ErrorNone); + + ilclient_create_component(st->client, &st->audio_render, "audio_render", ILCLIENT_ENABLE_INPUT_BUFFERS | ILCLIENT_DISABLE_ALL_PORTS); + assert(st->audio_render != NULL); + + st->list[0] = st->audio_render; + + // set up the number/size of buffers + memset(¶m, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + param.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + param.nVersion.nVersion = OMX_VERSION; + param.nPortIndex = 100; + + error = OMX_GetParameter(ILC_GET_HANDLE(st->audio_render), OMX_IndexParamPortDefinition, ¶m); + assert(error == OMX_ErrorNone); + + param.nBufferSize = size; + param.nBufferCountActual = num_buffers; + + error = OMX_SetParameter(ILC_GET_HANDLE(st->audio_render), OMX_IndexParamPortDefinition, ¶m); + assert(error == OMX_ErrorNone); + + // set the pcm parameters + memset(&pcm, 0, sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + pcm.nSize = sizeof(OMX_AUDIO_PARAM_PCMMODETYPE); + pcm.nVersion.nVersion = OMX_VERSION; + pcm.nPortIndex = 100; + pcm.nChannels = OUT_CHANNELS(num_channels); + pcm.eNumData = OMX_NumericalDataSigned; + pcm.eEndian = OMX_EndianLittle; + pcm.nSamplingRate = sample_rate; + pcm.bInterleaved = OMX_TRUE; + pcm.nBitPerSample = bit_depth; + pcm.ePCMMode = OMX_AUDIO_PCMModeLinear; + + switch(num_channels) { + case 1: + pcm.eChannelMapping[0] = OMX_AUDIO_ChannelCF; + break; + case 3: + pcm.eChannelMapping[2] = OMX_AUDIO_ChannelCF; + pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + break; + case 8: + pcm.eChannelMapping[7] = OMX_AUDIO_ChannelRS; + case 7: + pcm.eChannelMapping[6] = OMX_AUDIO_ChannelLS; + case 6: + pcm.eChannelMapping[5] = OMX_AUDIO_ChannelRR; + case 5: + pcm.eChannelMapping[4] = OMX_AUDIO_ChannelLR; + case 4: + pcm.eChannelMapping[3] = OMX_AUDIO_ChannelLFE; + pcm.eChannelMapping[2] = OMX_AUDIO_ChannelCF; + case 2: + pcm.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + pcm.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + break; + } + + error = OMX_SetParameter(ILC_GET_HANDLE(st->audio_render), OMX_IndexParamAudioPcm, &pcm); + assert(error == OMX_ErrorNone); + + ilclient_change_component_state(st->audio_render, OMX_StateIdle); + if(ilclient_enable_port_buffers(st->audio_render, 100, NULL, NULL, NULL) < 0) + { + // error + ilclient_change_component_state(st->audio_render, OMX_StateLoaded); + ilclient_cleanup_components(st->list); + + error = OMX_Deinit(); + assert(error == OMX_ErrorNone); + + ilclient_destroy(st->client); + + sem_destroy(&st->sema); + free(st); + *handle = NULL; + return -1; + } + + ilclient_change_component_state(st->audio_render, OMX_StateExecuting); + } + } + + return ret; +} + +int32_t audioplay_delete(AUDIOPLAY_STATE_T *st) +{ + OMX_ERRORTYPE error; + + ilclient_change_component_state(st->audio_render, OMX_StateIdle); + + error = OMX_SendCommand(ILC_GET_HANDLE(st->audio_render), OMX_CommandStateSet, OMX_StateLoaded, NULL); + assert(error == OMX_ErrorNone); + + ilclient_disable_port_buffers(st->audio_render, 100, st->user_buffer_list, NULL, NULL); + ilclient_change_component_state(st->audio_render, OMX_StateLoaded); + ilclient_cleanup_components(st->list); + + error = OMX_Deinit(); + assert(error == OMX_ErrorNone); + + ilclient_destroy(st->client); + + sem_destroy(&st->sema); + free(st); + + return 0; +} + +uint8_t *audioplay_get_buffer(AUDIOPLAY_STATE_T *st) +{ + OMX_BUFFERHEADERTYPE *hdr = NULL; + + hdr = ilclient_get_input_buffer(st->audio_render, 100, 0); + + if(hdr) + { + // put on the user list + sem_wait(&st->sema); + + hdr->pAppPrivate = st->user_buffer_list; + st->user_buffer_list = hdr; + + sem_post(&st->sema); + } + + return hdr ? hdr->pBuffer : NULL; +} + +int32_t audioplay_play_buffer(AUDIOPLAY_STATE_T *st, + uint8_t *buffer, + uint32_t length) +{ + OMX_BUFFERHEADERTYPE *hdr = NULL, *prev = NULL; + int32_t ret = -1; + + if(length % st->bytes_per_sample) + return ret; + + sem_wait(&st->sema); + + // search through user list for the right buffer header + hdr = st->user_buffer_list; + while(hdr != NULL && hdr->pBuffer != buffer && hdr->nAllocLen < length) + { + prev = hdr; + hdr = hdr->pAppPrivate; + } + + if(hdr) // we found it, remove from list + { + ret = 0; + if(prev) + prev->pAppPrivate = hdr->pAppPrivate; + else + st->user_buffer_list = hdr->pAppPrivate; + } + + sem_post(&st->sema); + + if(hdr) + { + OMX_ERRORTYPE error; + + hdr->pAppPrivate = NULL; + hdr->nOffset = 0; + hdr->nFilledLen = length; + + error = OMX_EmptyThisBuffer(ILC_GET_HANDLE(st->audio_render), hdr); + assert(error == OMX_ErrorNone); + } + + return ret; +} + +int32_t audioplay_set_dest(AUDIOPLAY_STATE_T *st, const char *name) +{ + int32_t success = -1; + OMX_CONFIG_BRCMAUDIODESTINATIONTYPE ar_dest; + + if (name && strlen(name) < sizeof(ar_dest.sName)) + { + OMX_ERRORTYPE error; + memset(&ar_dest, 0, sizeof(ar_dest)); + ar_dest.nSize = sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE); + ar_dest.nVersion.nVersion = OMX_VERSION; + strcpy((char *)ar_dest.sName, name); + + error = OMX_SetConfig(ILC_GET_HANDLE(st->audio_render), OMX_IndexConfigBrcmAudioDestination, &ar_dest); + assert(error == OMX_ErrorNone); + success = 0; + } + + return success; +} + + +uint32_t audioplay_get_latency(AUDIOPLAY_STATE_T *st) +{ + OMX_PARAM_U32TYPE param; + OMX_ERRORTYPE error; + + memset(¶m, 0, sizeof(OMX_PARAM_U32TYPE)); + param.nSize = sizeof(OMX_PARAM_U32TYPE); + param.nVersion.nVersion = OMX_VERSION; + param.nPortIndex = 100; + + error = OMX_GetConfig(ILC_GET_HANDLE(st->audio_render), OMX_IndexConfigAudioRenderingLatency, ¶m); + assert(error == OMX_ErrorNone); + + return param.nU32; +} + +#define CTTW_SLEEP_TIME 10 +#define MIN_LATENCY_TIME 20 + +static const char *audio_dest[] = {"local", "hdmi"}; +void play_api_test(int samplerate, int bitdepth, int nchannels, int dest) +{ + AUDIOPLAY_STATE_T *st; + int32_t ret; + unsigned int i, j, n; + int phase = 0; + int inc = 256<<16; + int dinc = 0; + int buffer_size = (BUFFER_SIZE_SAMPLES * bitdepth * OUT_CHANNELS(nchannels))>>3; + + assert(dest == 0 || dest == 1); + + ret = audioplay_create(&st, samplerate, nchannels, bitdepth, 10, buffer_size); + assert(ret == 0); + + ret = audioplay_set_dest(st, audio_dest[dest]); + assert(ret == 0); + + // iterate for 5 seconds worth of packets + for (n=0; n<((samplerate * 1000)/ BUFFER_SIZE_SAMPLES); n++) + { + uint8_t *buf; + int16_t *p; + uint32_t latency; + + while((buf = audioplay_get_buffer(st)) == NULL) + usleep(10*1000); + + p = (int16_t *) buf; + + // fill the buffer + for (i=0; i>16; + inc += dinc; + if (inc>>16 < 512) + dinc++; + else + dinc--; + + for(j=0; j (samplerate * (MIN_LATENCY_TIME + CTTW_SLEEP_TIME) / 1000)) + usleep(CTTW_SLEEP_TIME*1000); + + ret = audioplay_play_buffer(st, buf, buffer_size); + assert(ret == 0); + } + + audioplay_delete(st); +} + +int main (int argc, char **argv) +{ + // 0=headphones, 1=hdmi + int audio_dest = 0; + // audio sample rate in Hz + int samplerate = 48000; + // numnber of audio channels + int channels = 2; + // number of bits per sample + int bitdepth = 16; + bcm_host_init(); + + if (argc > 1) + audio_dest = atoi(argv[1]); + if (argc > 2) + channels = atoi(argv[2]); + if (argc > 3) + samplerate = atoi(argv[3]); + + printf("Outputting audio to %s\n", audio_dest==0 ? "analogue":"hdmi"); + + play_api_test(samplerate, bitdepth, channels, audio_dest); + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_audio/audioplay.h b/host_applications/linux/apps/hello_pi/hello_audio/audioplay.h new file mode 100755 index 0000000..9c96938 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_audio/audioplay.h @@ -0,0 +1,159 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// API for host applications to deliver raw PCM samples to rendered on VideoCore + +#ifndef AUDIOPLAY_H +#define AUDIOPLAY_H + +/** + * \file + * + * \brief This API allows the host to provide PCM samples to be + * rendered via audio_render. + * + * This file describes a simple API for host applications to play sound + * using VideoCore. It includes the functionality to: + * + * \li open/close + * \li set pcm parameters + * \li set buffer size parameters + * \li retrieve empty buffer available to use + * \li send full buffer to be played + * \li retrieve current buffering level + * + * This API has no thread context of it's own, so the caller must be + * aware that the IL API will be used in context. This has + * implications on executing calls inside callback contexts, and on + * the minimum size of stack that the caller requires. See the + * ilclient_stack_size() function for assistance. + * + * This API will use a single audio_render IL component, and + * supply buffers to the input port using the OpenMAX IL base profile mode. + ********************************************************************************/ + +struct AUDIOPLAY_STATE_T; + +/** + * The AUDIOPLAY_STATE_T is an opaque type that represents the + * audioplus engine handle. + *******************************************************************************/ +typedef struct AUDIOPLAY_STATE_T AUDIOPLAY_STATE_T; + +/** + * The audioplay_create() function creates the audioplay object. + * + * @param handle On success, this is filled in with a handle to use in other + * API functions. + * + * @param sample_rate The sample rate, in samples per second, for the PCM data. + * This shall be between 8000 and 96000. + * + * @param num_channels The number of channels for the PCM data. Must be 1, 2, 4, or 8. + * Channels must be sent interleaved. + * + * @param bit_depth The bitdepth per channel per sample. Must be 16 or 32. + * + * @param num_buffers The number of buffers that will be created to write PCM + * samples into. + * + * @param buffer_size The size in bytes of each buffer that will be used to write + * PCM samples into. Note that small buffers of less than a few + * Kb in size may be faster than larger buffers, although this is + * platform dependent. + * + * @return 0 on success, -1 on failure. + *********************************************************************************/ +VCHPRE_ int32_t VCHPOST_ audioplay_create(AUDIOPLAY_STATE_T **handle, + uint32_t sample_rate, + uint32_t num_channels, + uint32_t bit_depth, + uint32_t num_buffers, + uint32_t buffer_size); + +/** + * The audioplay_delete() function deletes the audioplay object. + * + * @param handle Must be a handle previously created by + * audioplay_create(). After calling this + * function that handle is no longer valid. Any + * buffers provided by audioplay_get_buffer() + * are also no longer valid and must not be referenced. + * + * @return 0 on success, -1 on failure. + ********************************************************************************/ +VCHPRE_ int32_t VCHPOST_ audioplay_delete(AUDIOPLAY_STATE_T *handle); + +/** + * The audioplay_get_buffer() function requests an empty + * buffer. Any buffer returned will have the valid size indicated in + * the call to audioplay_create(). + * + * @param handle Must be a handle previously created by + * audioplay_create(). + * + * @return A pointer to an available buffer. If no buffers are + * available, then NULL will be returned. + *********************************************************************************/ +VCHPRE_ uint8_t * VCHPOST_ audioplay_get_buffer(AUDIOPLAY_STATE_T *handle); + + +/** + * The audioplay_play_buffer() sends a buffer containing + * raw samples to be rendered. + * + * @param handle Must be a handle previously created by + * audioplay_create(). + * + * @param buffer Must be a pointer previously returned by + * audioplay_get_buffer(). After calling this function + * the buffer pointer must not be referenced until returned + * again by another call to audioplay_get_buffer(). + * + * @param length Length in bytes of valid data. Must be a whole number of + * samples, ie a multiple of (num_channels*bit_depth/8). + * + * @return 0 on success, -1 on failure + ********************************************************************************/ +VCHPRE_ int32_t VCHPOST_ audioplay_play_buffer(AUDIOPLAY_STATE_T *handle, + uint8_t *buffer, + uint32_t length); + +/** + * The audioplay_get_latency() requests the current audio + * playout buffer size in samples, which is the latency until the next + * sample supplied is to be rendered. + * + * @param handle Must be a handle previously created by + * audioplay_create(). + * + * @return Number of samples currently buffered. + *********************************************************************************/ +VCHPRE_ uint32_t VCHPOST_ audioplay_get_latency(AUDIOPLAY_STATE_T *handle); + + +#endif /* AUDIOPLAY_H */ diff --git a/host_applications/linux/apps/hello_pi/hello_audio/sinewave.c b/host_applications/linux/apps/hello_pi/hello_audio/sinewave.c new file mode 100755 index 0000000..10668b5 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_audio/sinewave.c @@ -0,0 +1,160 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Lookup table for audio output demo + +short Sinewave[] = { + 0, 201, 402, 603, 804, 1005, 1206, 1406, + 1607, 1808, 2009, 2209, 2410, 2610, 2811, 3011, + 3211, 3411, 3611, 3811, 4011, 4210, 4409, 4608, + 4807, 5006, 5205, 5403, 5601, 5799, 5997, 6195, + 6392, 6589, 6786, 6982, 7179, 7375, 7571, 7766, + 7961, 8156, 8351, 8545, 8739, 8932, 9126, 9319, + 9511, 9703, 9895, 10087, 10278, 10469, 10659, 10849, + 11038, 11227, 11416, 11604, 11792, 11980, 12166, 12353, + 12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827, + 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268, + 15446, 15623, 15799, 15975, 16150, 16325, 16499, 16672, + 16845, 17017, 17189, 17360, 17530, 17699, 17868, 18036, + 18204, 18371, 18537, 18702, 18867, 19031, 19194, 19357, + 19519, 19680, 19840, 20000, 20159, 20317, 20474, 20631, + 20787, 20942, 21096, 21249, 21402, 21554, 21705, 21855, + 22004, 22153, 22301, 22448, 22594, 22739, 22883, 23027, + 23169, 23311, 23452, 23592, 23731, 23869, 24006, 24143, + 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201, + 25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198, + 26318, 26437, 26556, 26673, 26789, 26905, 27019, 27132, + 27244, 27355, 27466, 27575, 27683, 27790, 27896, 28001, + 28105, 28208, 28309, 28410, 28510, 28608, 28706, 28802, + 28897, 28992, 29085, 29177, 29268, 29358, 29446, 29534, + 29621, 29706, 29790, 29873, 29955, 30036, 30116, 30195, + 30272, 30349, 30424, 30498, 30571, 30643, 30713, 30783, + 30851, 30918, 30984, 31049, + 31113, 31175, 31236, 31297, + 31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735, + 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097, + 32137, 32176, 32213, 32249, 32284, 32318, 32350, 32382, + 32412, 32441, 32468, 32495, 32520, 32544, 32567, 32588, + 32609, 32628, 32646, 32662, 32678, 32692, 32705, 32717, + 32727, 32736, 32744, 32751, 32757, 32761, 32764, 32766, + 32767, 32766, 32764, 32761, 32757, 32751, 32744, 32736, + 32727, 32717, 32705, 32692, 32678, 32662, 32646, 32628, + 32609, 32588, 32567, 32544, 32520, 32495, 32468, 32441, + 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176, + 32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833, + 31785, 31735, 31684, 31633, 31580, 31525, 31470, 31413, + 31356, 31297, 31236, 31175, 31113, 31049, 30984, 30918, + 30851, 30783, 30713, 30643, 30571, 30498, 30424, 30349, + 30272, 30195, 30116, 30036, 29955, 29873, 29790, 29706, + 29621, 29534, 29446, 29358, 29268, 29177, 29085, 28992, + 28897, 28802, 28706, 28608, 28510, 28410, 28309, 28208, + 28105, 28001, 27896, 27790, 27683, 27575, 27466, 27355, + 27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437, + 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456, + 25329, 25201, 25072, 24942, 24811, 24679, 24546, 24413, + 24278, 24143, 24006, 23869, 23731, 23592, 23452, 23311, + 23169, 23027, 22883, 22739, 22594, 22448, 22301, 22153, + 22004, 21855, 21705, 21554, 21402, 21249, 21096, 20942, + 20787, 20631, 20474, 20317, 20159, 20000, 19840, 19680, + 19519, 19357, 19194, 19031, 18867, 18702, 18537, 18371, + 18204, 18036, 17868, 17699, 17530, 17360, 17189, 17017, + 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623, + 15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191, + 14009, 13827, 13645, 13462, 13278, 13094, 12909, 12724, + 12539, 12353, 12166, 11980, 11792, 11604, 11416, 11227, + 11038, 10849, 10659, 10469, 10278, 10087, 9895, 9703, + 9511, 9319, 9126, 8932, 8739, 8545, 8351, 8156, + 7961, 7766, 7571, 7375, 7179, 6982, 6786, 6589, + 6392, 6195, 5997, 5799, 5601, 5403, 5205, 5006, + 4807, 4608, 4409, 4210, 4011, 3811, 3611, 3411, + 3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808, + 1607, 1406, 1206, 1005, 804, 603, 402, 201, + 0, -201, -402, -603, -804, -1005, -1206, -1406, + -1607, -1808, -2009, -2209, -2410, -2610, -2811, -3011, + -3211, -3411, -3611, -3811, -4011, -4210, -4409, -4608, + -4807, -5006, -5205, -5403, -5601, -5799, -5997, -6195, + -6392, -6589, -6786, -6982, -7179, -7375, -7571, -7766, + -7961, -8156, -8351, -8545, -8739, -8932, -9126, -9319, + -9511, -9703, -9895, -10087, -10278, -10469, -10659, -10849, + -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353, + -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827, + -14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268, + -15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672, + -16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036, + -18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357, + -19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631, + -20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855, + -22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027, + -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143, + -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201, + -25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198, + -26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132, + -27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001, + -28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802, + -28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534, + -29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195, + -30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783, + -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297, + -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735, + -31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097, + -32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382, + -32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588, + -32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717, + -32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766, + -32767, -32766, -32764, -32761, -32757, -32751, -32744, -32736, + -32727, -32717, -32705, -32692, -32678, -32662, -32646, -32628, + -32609, -32588, -32567, -32544, -32520, -32495, -32468, -32441, + -32412, -32382, -32350, -32318, -32284, -32249, -32213, -32176, + -32137, -32097, -32056, -32014, -31970, -31926, -31880, -31833, + -31785, -31735, -31684, -31633, -31580, -31525, -31470, -31413, + -31356, -31297, -31236, -31175, -31113, -31049, -30984, -30918, + -30851, -30783, -30713, -30643, -30571, -30498, -30424, -30349, + -30272, -30195, -30116, -30036, -29955, -29873, -29790, -29706, + -29621, -29534, -29446, -29358, -29268, -29177, -29085, -28992, + -28897, -28802, -28706, -28608, -28510, -28410, -28309, -28208, + -28105, -28001, -27896, -27790, -27683, -27575, -27466, -27355, + -27244, -27132, -27019, -26905, -26789, -26673, -26556, -26437, + -26318, -26198, -26077, -25954, -25831, -25707, -25582, -25456, + -25329, -25201, -25072, -24942, -24811, -24679, -24546, -24413, + -24278, -24143, -24006, -23869, -23731, -23592, -23452, -23311, + -23169, -23027, -22883, -22739, -22594, -22448, -22301, -22153, + -22004, -21855, -21705, -21554, -21402, -21249, -21096, -20942, + -20787, -20631, -20474, -20317, -20159, -20000, -19840, -19680, + -19519, -19357, -19194, -19031, -18867, -18702, -18537, -18371, + -18204, -18036, -17868, -17699, -17530, -17360, -17189, -17017, + -16845, -16672, -16499, -16325, -16150, -15975, -15799, -15623, + -15446, -15268, -15090, -14911, -14732, -14552, -14372, -14191, + -14009, -13827, -13645, -13462, -13278, -13094, -12909, -12724, + -12539, -12353, -12166, -11980, -11792, -11604, -11416, -11227, + -11038, -10849, -10659, -10469, -10278, -10087, -9895, -9703, + -9511, -9319, -9126, -8932, -8739, -8545, -8351, -8156, + -7961, -7766, -7571, -7375, -7179, -6982, -6786, -6589, + -6392, -6195, -5997, -5799, -5601, -5403, -5205, -5006, + -4807, -4608, -4409, -4210, -4011, -3811, -3611, -3411, + -3211, -3011, -2811, -2610, -2410, -2209, -2009, -1808, + -1607, -1406, -1206, -1005, -804, -603, -402, -201, +}; diff --git a/host_applications/linux/apps/hello_pi/hello_dispmanx/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_dispmanx/CMakeLists.txt new file mode 100755 index 0000000..0471a1d --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_dispmanx/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_dispmanx.bin) +set(SRCS dispmanx.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_dispmanx/Makefile b/host_applications/linux/apps/hello_pi/hello_dispmanx/Makefile new file mode 100755 index 0000000..98dc5e9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_dispmanx/Makefile @@ -0,0 +1,5 @@ +OBJS=dispmanx.o +BIN=hello_dispmanx.bin + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_dispmanx/dispmanx.c b/host_applications/linux/apps/hello_pi/hello_dispmanx/dispmanx.c new file mode 100755 index 0000000..1f23b32 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_dispmanx/dispmanx.c @@ -0,0 +1,163 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// A simple demo using dispmanx to display an overlay + +#include +#include +#include +#include +#include +#include + +#include "bcm_host.h" + +#define WIDTH 200 +#define HEIGHT 200 + +#ifndef ALIGN_UP +#define ALIGN_UP(x,y) ((x + (y)-1) & ~((y)-1)) +#endif + +typedef struct +{ + DISPMANX_DISPLAY_HANDLE_T display; + DISPMANX_MODEINFO_T info; + void *image; + DISPMANX_UPDATE_HANDLE_T update; + DISPMANX_RESOURCE_HANDLE_T resource; + DISPMANX_ELEMENT_HANDLE_T element; + uint32_t vc_image_ptr; + +} RECT_VARS_T; + +static RECT_VARS_T gRectVars; + +static void FillRect( VC_IMAGE_TYPE_T type, void *image, int pitch, int aligned_height, int x, int y, int w, int h, int val ) +{ + int row; + int col; + + uint16_t *line = (uint16_t *)image + y * (pitch>>1) + x; + + for ( row = 0; row < h; row++ ) + { + for ( col = 0; col < w; col++ ) + { + line[col] = val; + } + line += (pitch>>1); + } +} + +int main(void) +{ + RECT_VARS_T *vars; + uint32_t screen = 0; + int ret; + VC_RECT_T src_rect; + VC_RECT_T dst_rect; + VC_IMAGE_TYPE_T type = VC_IMAGE_RGB565; + int width = WIDTH, height = HEIGHT; + int pitch = ALIGN_UP(width*2, 32); + int aligned_height = ALIGN_UP(height, 16); + VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, + 120, /*alpha 0->255*/ + 0 }; + + vars = &gRectVars; + + bcm_host_init(); + + printf("Open display[%i]...\n", screen ); + vars->display = vc_dispmanx_display_open( screen ); + + ret = vc_dispmanx_display_get_info( vars->display, &vars->info); + assert(ret == 0); + printf( "Display is %d x %d\n", vars->info.width, vars->info.height ); + + vars->image = calloc( 1, pitch * height ); + assert(vars->image); + + FillRect( type, vars->image, pitch, aligned_height, 0, 0, width, height, 0xFFFF ); + FillRect( type, vars->image, pitch, aligned_height, 0, 0, width, height, 0xF800 ); + FillRect( type, vars->image, pitch, aligned_height, 20, 20, width - 40, height - 40, 0x07E0 ); + FillRect( type, vars->image, pitch, aligned_height, 40, 40, width - 80, height - 80, 0x001F ); + + vars->resource = vc_dispmanx_resource_create( type, + width, + height, + &vars->vc_image_ptr ); + assert( vars->resource ); + vc_dispmanx_rect_set( &dst_rect, 0, 0, width, height); + ret = vc_dispmanx_resource_write_data( vars->resource, + type, + pitch, + vars->image, + &dst_rect ); + assert( ret == 0 ); + vars->update = vc_dispmanx_update_start( 10 ); + assert( vars->update ); + + vc_dispmanx_rect_set( &src_rect, 0, 0, width << 16, height << 16 ); + + vc_dispmanx_rect_set( &dst_rect, ( vars->info.width - width ) / 2, + ( vars->info.height - height ) / 2, + width, + height ); + + vars->element = vc_dispmanx_element_add( vars->update, + vars->display, + 2000, // layer + &dst_rect, + vars->resource, + &src_rect, + DISPMANX_PROTECTION_NONE, + &alpha, + NULL, // clamp + VC_IMAGE_ROT0 ); + + ret = vc_dispmanx_update_submit_sync( vars->update ); + assert( ret == 0 ); + + printf( "Sleeping for 10 seconds...\n" ); + sleep( 10 ); + + vars->update = vc_dispmanx_update_start( 10 ); + assert( vars->update ); + ret = vc_dispmanx_element_remove( vars->update, vars->element ); + assert( ret == 0 ); + ret = vc_dispmanx_update_submit_sync( vars->update ); + assert( ret == 0 ); + ret = vc_dispmanx_resource_delete( vars->resource ); + assert( ret == 0 ); + ret = vc_dispmanx_display_close( vars->display ); + assert( ret == 0 ); + + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_encode/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_encode/CMakeLists.txt new file mode 100755 index 0000000..147623b --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_encode/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_encode.bin) +set(SRCS encode.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_encode/Makefile b/host_applications/linux/apps/hello_pi/hello_encode/Makefile new file mode 100755 index 0000000..62d320e --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_encode/Makefile @@ -0,0 +1,6 @@ +OBJS=encode.o +BIN=hello_encode.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_encode/encode.c b/host_applications/linux/apps/hello_pi/hello_encode/encode.c new file mode 100755 index 0000000..7acb54b --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_encode/encode.c @@ -0,0 +1,320 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, Kalle Vahlman + Tuomas Kulve +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Video encode demo using OpenMAX IL though the ilcient helper library + +#include +#include +#include + +#include "bcm_host.h" +#include "ilclient.h" + +#define NUMFRAMES 300 +#define WIDTH 640 +#define PITCH ((WIDTH+31)&~31) +#define HEIGHT ((WIDTH)*9/16) +#define HEIGHT16 ((HEIGHT+15)&~15) +#define SIZE ((WIDTH * HEIGHT16 * 3)/2) + +// generate an animated test card in YUV format +static int +generate_test_card(void *buf, OMX_U32 * filledLen, int frame) +{ + int i, j; + char *y = buf, *u = y + PITCH * HEIGHT16, *v = + u + (PITCH >> 1) * (HEIGHT16 >> 1); + + for (j = 0; j < HEIGHT / 2; j++) { + char *py = y + 2 * j * PITCH; + char *pu = u + j * (PITCH >> 1); + char *pv = v + j * (PITCH >> 1); + for (i = 0; i < WIDTH / 2; i++) { + int z = (((i + frame) >> 4) ^ ((j + frame) >> 4)) & 15; + py[0] = py[1] = py[PITCH] = py[PITCH + 1] = 0x80 + z * 0x8; + pu[0] = 0x00 + z * 0x10; + pv[0] = 0x80 + z * 0x30; + py += 2; + pu++; + pv++; + } + } + *filledLen = SIZE; + return 1; +} + +static void +print_def(OMX_PARAM_PORTDEFINITIONTYPE def) +{ + printf("Port %u: %s %u/%u %u %u %s,%s,%s %ux%u %ux%u @%u %u\n", + def.nPortIndex, + def.eDir == OMX_DirInput ? "in" : "out", + def.nBufferCountActual, + def.nBufferCountMin, + def.nBufferSize, + def.nBufferAlignment, + def.bEnabled ? "enabled" : "disabled", + def.bPopulated ? "populated" : "not pop.", + def.bBuffersContiguous ? "contig." : "not cont.", + def.format.video.nFrameWidth, + def.format.video.nFrameHeight, + def.format.video.nStride, + def.format.video.nSliceHeight, + def.format.video.xFramerate, def.format.video.eColorFormat); +} + +static int +video_encode_test(char *outputfilename) +{ + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + OMX_PARAM_PORTDEFINITIONTYPE def; + COMPONENT_T *video_encode = NULL; + COMPONENT_T *list[5]; + OMX_BUFFERHEADERTYPE *buf; + OMX_BUFFERHEADERTYPE *out; + OMX_ERRORTYPE r; + ILCLIENT_T *client; + int status = 0; + int framenumber = 0; + FILE *outf; + + memset(list, 0, sizeof(list)); + + if ((client = ilclient_init()) == NULL) { + return -3; + } + + if (OMX_Init() != OMX_ErrorNone) { + ilclient_destroy(client); + return -4; + } + + // create video_encode + r = ilclient_create_component(client, &video_encode, "video_encode", + ILCLIENT_DISABLE_ALL_PORTS | + ILCLIENT_ENABLE_INPUT_BUFFERS | + ILCLIENT_ENABLE_OUTPUT_BUFFERS); + if (r != 0) { + printf + ("ilclient_create_component() for video_encode failed with %x!\n", + r); + exit(1); + } + list[0] = video_encode; + + // get current settings of video_encode component from port 200 + memset(&def, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + def.nVersion.nVersion = OMX_VERSION; + def.nPortIndex = 200; + + if (OMX_GetParameter + (ILC_GET_HANDLE(video_encode), OMX_IndexParamPortDefinition, + &def) != OMX_ErrorNone) { + printf("%s:%d: OMX_GetParameter() for video_encode port 200 failed!\n", + __FUNCTION__, __LINE__); + exit(1); + } + + print_def(def); + + // Port 200: in 1/1 115200 16 enabled,not pop.,not cont. 320x240 320x240 @1966080 20 + def.format.video.nFrameWidth = WIDTH; + def.format.video.nFrameHeight = HEIGHT; + def.format.video.xFramerate = 30 << 16; + def.format.video.nSliceHeight = def.format.video.nFrameHeight; + def.format.video.nStride = def.format.video.nFrameWidth; + def.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar; + + print_def(def); + + r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), + OMX_IndexParamPortDefinition, &def); + if (r != OMX_ErrorNone) { + printf + ("%s:%d: OMX_SetParameter() for video_encode port 200 failed with %x!\n", + __FUNCTION__, __LINE__, r); + exit(1); + } + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 201; + format.eCompressionFormat = OMX_VIDEO_CodingAVC; + + printf("OMX_SetParameter for video_encode:201...\n"); + r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), + OMX_IndexParamVideoPortFormat, &format); + if (r != OMX_ErrorNone) { + printf + ("%s:%d: OMX_SetParameter() for video_encode port 201 failed with %x!\n", + __FUNCTION__, __LINE__, r); + exit(1); + } + + OMX_VIDEO_PARAM_BITRATETYPE bitrateType; + // set current bitrate to 1Mbit + memset(&bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE)); + bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE); + bitrateType.nVersion.nVersion = OMX_VERSION; + bitrateType.eControlRate = OMX_Video_ControlRateVariable; + bitrateType.nTargetBitrate = 1000000; + bitrateType.nPortIndex = 201; + r = OMX_SetParameter(ILC_GET_HANDLE(video_encode), + OMX_IndexParamVideoBitrate, &bitrateType); + if (r != OMX_ErrorNone) { + printf + ("%s:%d: OMX_SetParameter() for bitrate for video_encode port 201 failed with %x!\n", + __FUNCTION__, __LINE__, r); + exit(1); + } + + + // get current bitrate + memset(&bitrateType, 0, sizeof(OMX_VIDEO_PARAM_BITRATETYPE)); + bitrateType.nSize = sizeof(OMX_VIDEO_PARAM_BITRATETYPE); + bitrateType.nVersion.nVersion = OMX_VERSION; + bitrateType.nPortIndex = 201; + + if (OMX_GetParameter + (ILC_GET_HANDLE(video_encode), OMX_IndexParamVideoBitrate, + &bitrateType) != OMX_ErrorNone) { + printf("%s:%d: OMX_GetParameter() for video_encode for bitrate port 201 failed!\n", + __FUNCTION__, __LINE__); + exit(1); + } + printf("Current Bitrate=%u\n",bitrateType.nTargetBitrate); + + + + printf("encode to idle...\n"); + if (ilclient_change_component_state(video_encode, OMX_StateIdle) == -1) { + printf + ("%s:%d: ilclient_change_component_state(video_encode, OMX_StateIdle) failed", + __FUNCTION__, __LINE__); + } + + printf("enabling port buffers for 200...\n"); + if (ilclient_enable_port_buffers(video_encode, 200, NULL, NULL, NULL) != 0) { + printf("enabling port buffers for 200 failed!\n"); + exit(1); + } + + printf("enabling port buffers for 201...\n"); + if (ilclient_enable_port_buffers(video_encode, 201, NULL, NULL, NULL) != 0) { + printf("enabling port buffers for 201 failed!\n"); + exit(1); + } + + printf("encode to executing...\n"); + ilclient_change_component_state(video_encode, OMX_StateExecuting); + + outf = fopen(outputfilename, "w"); + if (outf == NULL) { + printf("Failed to open '%s' for writing video\n", outputfilename); + exit(1); + } + + printf("looping for buffers...\n"); + do { + buf = ilclient_get_input_buffer(video_encode, 200, 1); + if (buf == NULL) { + printf("Doh, no buffers for me!\n"); + } + else { + /* fill it */ + generate_test_card(buf->pBuffer, &buf->nFilledLen, framenumber++); + + if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_encode), buf) != + OMX_ErrorNone) { + printf("Error emptying buffer!\n"); + } + + out = ilclient_get_output_buffer(video_encode, 201, 1); + + r = OMX_FillThisBuffer(ILC_GET_HANDLE(video_encode), out); + if (r != OMX_ErrorNone) { + printf("Error filling buffer: %x\n", r); + } + + if (out != NULL) { + if (out->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { + int i; + for (i = 0; i < out->nFilledLen; i++) + printf("%x ", out->pBuffer[i]); + printf("\n"); + } + + r = fwrite(out->pBuffer, 1, out->nFilledLen, outf); + if (r != out->nFilledLen) { + printf("fwrite: Error emptying buffer: %d!\n", r); + } + else { + printf("Writing frame %d/%d\n", framenumber, NUMFRAMES); + } + out->nFilledLen = 0; + } + else { + printf("Not getting it :(\n"); + } + + } + } + while (framenumber < NUMFRAMES); + + fclose(outf); + + printf("Teardown.\n"); + + printf("disabling port buffers for 200 and 201...\n"); + ilclient_disable_port_buffers(video_encode, 200, NULL, NULL, NULL); + ilclient_disable_port_buffers(video_encode, 201, NULL, NULL, NULL); + + ilclient_state_transition(list, OMX_StateIdle); + ilclient_state_transition(list, OMX_StateLoaded); + + ilclient_cleanup_components(list); + + OMX_Deinit(); + + ilclient_destroy(client); + return status; +} + +int +main(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + exit(1); + } + bcm_host_init(); + return video_encode_test(argv[1]); +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.c b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.c new file mode 100755 index 0000000..5bda67b --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.c @@ -0,0 +1,135 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "gpu_fft.h" + +#define GPU_FFT_BUSY_WAIT_LIMIT (5<<12) // ~1ms + +typedef struct GPU_FFT_COMPLEX COMPLEX; + +int gpu_fft_prepare( + int mb, // mailbox file_desc + int log2_N, // log2(FFT_length) = 8...22 + int direction, // GPU_FFT_FWD: fft(); GPU_FFT_REV: ifft() + int jobs, // number of transforms in batch + struct GPU_FFT **fft) { + + unsigned info_bytes, twid_bytes, data_bytes, code_bytes, unif_bytes, mail_bytes; + unsigned size, *uptr, vc_tw, vc_data; + int i, q, shared, unique, passes, ret; + + struct GPU_FFT_BASE *base; + struct GPU_FFT_PTR ptr; + struct GPU_FFT *info; + + if (gpu_fft_twiddle_size(log2_N, &shared, &unique, &passes)) return -2; + + info_bytes = 4096; + data_bytes = (1+((sizeof(COMPLEX)<x = 1<y = jobs; + + // Ping-pong buffers leave results in or out of place + info->in = info->out = ptr.arm.cptr; + info->step = data_bytes / sizeof(COMPLEX); + if (passes&1) info->out += info->step * jobs; // odd => out of place + vc_data = gpu_fft_ptr_inc(&ptr, data_bytes*jobs*2); + + // Shader code + memcpy(ptr.arm.vptr, gpu_fft_shader_code(log2_N), code_bytes); + base->vc_code = gpu_fft_ptr_inc(&ptr, code_bytes); + + // Twiddles + gpu_fft_twiddle_data(log2_N, direction, ptr.arm.fptr); + vc_tw = gpu_fft_ptr_inc(&ptr, twid_bytes); + + uptr = ptr.arm.uptr; + + // Uniforms + for (q=0; qvc_unifs[q] = gpu_fft_ptr_inc(&ptr, sizeof(int)*(5+jobs*2)); + } + + if ((jobs<vc_msg = 0; + } + else { + // Mailbox message + for (q=0; qvc_unifs[q]; + *uptr++ = base->vc_code; + } + + base->vc_msg = ptr.vc; + } + + *fft = info; + return 0; +} + +unsigned gpu_fft_execute(struct GPU_FFT *info) { + return gpu_fft_base_exec(&info->base, GPU_FFT_QPUS); +} + +void gpu_fft_release(struct GPU_FFT *info) { + gpu_fft_base_release(&info->base); +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.h b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.h new file mode 100755 index 0000000..8b1cb08 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.h @@ -0,0 +1,101 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __GPU_FFT__ +#define __GPU_FFT__ + +#define GPU_FFT_QPUS 8 + +#define GPU_FFT_PI 3.14159265358979323846 + +#define GPU_FFT_FWD 0 // forward FFT +#define GPU_FFT_REV 1 // inverse FFT + +struct GPU_FFT_COMPLEX { + float re, im; +}; + +struct GPU_FFT_PTR { + unsigned vc; + union { struct GPU_FFT_COMPLEX *cptr; + void *vptr; + char *bptr; + float *fptr; + unsigned *uptr; } arm; +}; + +struct GPU_FFT_BASE { + int mb; + unsigned handle, size, vc_msg, vc_code, vc_unifs[GPU_FFT_QPUS], peri_size; + volatile unsigned *peri; +}; + +struct GPU_FFT { + struct GPU_FFT_BASE base; + struct GPU_FFT_COMPLEX *in, *out; + int x, y, step; +}; + +int gpu_fft_prepare( + int mb, // mailbox file_desc + int log2_N, // log2(FFT_length) = 8...22 + int direction, // GPU_FFT_FWD: fft(); GPU_FFT_REV: ifft() + int jobs, // number of transforms in batch + struct GPU_FFT **fft); + +unsigned gpu_fft_execute( + struct GPU_FFT *info); + +void gpu_fft_release( + struct GPU_FFT *info); + +// private +int gpu_fft_twiddle_size(int, int *, int *, int *); +void gpu_fft_twiddle_data(int, int, float *); +unsigned int gpu_fft_shader_size(int); +unsigned int *gpu_fft_shader_code(int); + +// gpu_fft_base: + +unsigned gpu_fft_base_exec ( + struct GPU_FFT_BASE *base, + int num_qpus); + +int gpu_fft_alloc ( + int mb, + unsigned size, + struct GPU_FFT_PTR *ptr); + +void gpu_fft_base_release( + struct GPU_FFT_BASE *base); + +unsigned gpu_fft_ptr_inc ( + struct GPU_FFT_PTR *ptr, + int bytes); + +#endif // __GPU_FFT__ diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.txt b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.txt new file mode 100755 index 0000000..49d65bf --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.txt @@ -0,0 +1,159 @@ +BCM2835 "GPU_FFT" release 3.0 by Andrew Holme, 2015. + +GPU_FFT is an FFT library for the Raspberry Pi which exploits the BCM2835 SoC +3D hardware to deliver ten times more data throughput than is possible on the +700 MHz ARM of the Pi 1. Kernels are provided for all power-of-2 FFT lengths +between 256 and 4,194,304 points inclusive. A transpose function, which also +uses the 3D hardware, is provided to support 2-dimensional transforms. + + +*** Accuracy *** + +GPU_FFT uses single-precision floats for data and twiddle factors. The output +is not scaled. The relative root-mean-square (rms) error in parts-per-million +(ppm) for different transform lengths (N) is typically: + +log2(N) | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 +ppm rms | 0.33 | 0.46 | 0.52 | 0.59 | 0.78 | 0.83 | 0.92 | 0.98 + +log2(N) | 16 | 17 | 18 | 19 | 20 | 21 | 22 +ppm rms | 1.0 | 1.3 | 1.3 | 1.4 | 1.5 | 1.5 | 1.5 + +Accuracy has improved significantly over previous releases at the expense of a +small (2%) performance hit; however, FFTW is still one order of magnitude more +accurate than GPU_FFT. + + +*** Throughput *** + +GPU_FFT 1.0 had to be invoked through a "mailbox" which added a 100us overhead +on every call. To mitigate this, batches of transforms could be submitted via +a single call. GPU_FFT now avoids this 100us overhead by poking GPU registers +directly from the ARM if total batch runtime will be short; but still uses the +mailbox for longer jobs to avoid busy waiting at 100% CPU for too long. + +Typical per-transform runtimes for batch sizes of 1 and 10; and comparative +figures for FFTW (FFTW_MEASURE mode) on a Pi 1 with L2 cache enabled are: + +log2(N) | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 + 1 | 0.033 | 0.049 | 0.070 | 0.12 | 0.25 | 0.61 | 1.2 | 3.5 + 10 | 0.017 | 0.029 | 0.049 | 0.11 | 0.27 | 0.66 | 1.2 | 3.3 + FFTW | 0.092 | 0.22 | 0.48 | 0.95 | 3.0 | 5.1 | 12 | 31 + +log2(N) | 16 | 17 | 18 | 19 | 20 | 21 | 22 All times in + 1 | 7.0 | 17 | 43 | 97 | 194 | 388 | 786 milliseconds + FFTW | 83 | 180 | 560 | 670 | 1600 | 3400 | 8800 2 sig. figs. + + +*** API functions *** + + gpu_fft_prepare() Call once to allocate memory and initialise data + structures. Returns 0 for success. + + gpu_fft_execute() Call one or more times to execute a previously + prepared FFT batch. Returns 0 for success. + + gpu_fft_release() Call once to release resources after use. + GPU memory is permanently lost if not freed. + + +*** Parameters *** + + int mb Mailbox file descriptor obtained by calling mbox_open() + + int log2_N log2(FFT length) = 8 to 22 + + int direction FFT direction: GPU_FFT_FWD for forward FFT + GPU_FFT_REV for inverse FFT + + int jobs Number of transforms in batch = 1 or more + + GPU_FFT ** Output parameter from prepare: control structure. + GPU_FFT * Input parameter to execute and release + + +*** Data format *** + +Complex data arrays are stored as alternate real and imaginary parts: + + struct GPU_FFT_COMPLEX { + float re, im; + }; + +The GPU_FFT struct created by gpu_fft_prepare() contains pointers to the input +and output arrays: + + struct GPU_FFT { + struct GPU_FFT_COMPLEX *in, *out; + +When executing a batch of transforms, buffer pointers are obtained as follows: + + struct GPU_FFT *fft = gpu_fft_prepare( ... , jobs); + for (int j=0; jin + j*fft->step; + struct GPU_FFT_COMPLEX *out = fft->out + j*fft->step; + +GPU_FFT.step is greater than FFT length because a guard space is left between +buffers for caching and alignment reasons. + +GPU_FFT performs multiple passes between ping-pong buffers. The final output +lands in the same buffer as input after an even number of passes. Transforms +where log2_N=12...16 use an odd number of passes and the final result is left +out-of-place. The input data is never preserved. + + +*** Example program *** + +The code that produced the above accuracy and performance figures is included +as a demo with the latest Raspbian distro. Build and run it as follows: + +cd /opt/vc/src/hello_pi/hello_fft +make +sudo ./hello_fft.bin 12 + +It accepts three optional command-line arguments: + +The special character device is required for the ioctl mailbox through which +the ARM communicates with the Videocore GPU. + + +*** With Open GL on Pi 1 *** + +GPU_FFT and Open GL will run concurrently on Pi 1 if GPU_FFT is configured not +to use VC4 L2 cache by zeroing a define in file gpu_fft_base.c as follows: + +#define GPU_FFT_USE_VC4_L2_CACHE 0 // Pi 1 only: cached=1; direct=0 + +Overall performance will probably be higher if GPU_FFT and Open GL take turns +at using the 3D hardware. Since eglSwapBuffers() returns immediately without +waiting for rendering, call glFlush() and glFinish() afterwards as follows: + + for (;;) { + .... + eglSwapBuffers(....); // non-blocking call returns immediately + glFlush(); + glFinish(); // wait until V3D hardware is idle + .... + gpu_fft_execute(....); // blocking call + .... + } + + +*** 2-dimensional FFT *** + +Please study the hello_fft_2d demo source, which is built and executed thus: + +make hello_fft_2d.bin +sudo ./hello_fft_2d.bin + +This generates a Windows BMP file: "hello_fft_2d.bmp" + +The demo uses a square 512x512 array; however, rectangular arrays are allowed. +The following lines in gpu_fft_trans.c will do what is safe: + + ptr.arm.uptr[6] = src->x < dst->y? src->x : dst->y; + ptr.arm.uptr[7] = src->y < dst->x? src->y : dst->x; + +One may transpose the output from the second FFT pass back into the first pass +input buffer, by preparing and executing a second transposition; however, this +is probably unnecessary. It depends on how the final output will be accessed. diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_base.c b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_base.c new file mode 100755 index 0000000..76656b8 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_base.c @@ -0,0 +1,190 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "gpu_fft.h" +#include "mailbox.h" + +#define BUS_TO_PHYS(x) ((x)&~0xC0000000) + +// V3D spec: http://www.broadcom.com/docs/support/videocore/VideoCoreIV-AG100-R.pdf +#define V3D_L2CACTL (0xC00020>>2) +#define V3D_SLCACTL (0xC00024>>2) +#define V3D_SRQPC (0xC00430>>2) +#define V3D_SRQUA (0xC00434>>2) +#define V3D_SRQCS (0xC0043c>>2) +#define V3D_DBCFG (0xC00e00>>2) +#define V3D_DBQITE (0xC00e2c>>2) +#define V3D_DBQITC (0xC00e30>>2) + +// Setting this define to zero on Pi 1 allows GPU_FFT and Open GL +// to co-exist and also improves performance of longer transforms: +#define GPU_FFT_USE_VC4_L2_CACHE 1 // Pi 1 only: cached=1; direct=0 + +#define GPU_FFT_NO_FLUSH 1 +#define GPU_FFT_TIMEOUT 2000 // ms + +struct GPU_FFT_HOST { + unsigned mem_flg, mem_map, peri_addr, peri_size; +}; + +int gpu_fft_get_host_info(struct GPU_FFT_HOST *info) { + void *handle; + unsigned (*bcm_host_get_sdram_address) (void); + unsigned (*bcm_host_get_peripheral_address)(void); + unsigned (*bcm_host_get_peripheral_size) (void); + + // Pi 1 defaults + info->peri_addr = 0x20000000; + info->peri_size = 0x01000000; + info->mem_flg = GPU_FFT_USE_VC4_L2_CACHE? 0xC : 0x4; + info->mem_map = GPU_FFT_USE_VC4_L2_CACHE? 0x0 : 0x20000000; // Pi 1 only + + handle = dlopen("libbcm_host.so", RTLD_LAZY); + if (!handle) return -1; + + *(void **) (&bcm_host_get_sdram_address) = dlsym(handle, "bcm_host_get_sdram_address"); + *(void **) (&bcm_host_get_peripheral_address) = dlsym(handle, "bcm_host_get_peripheral_address"); + *(void **) (&bcm_host_get_peripheral_size) = dlsym(handle, "bcm_host_get_peripheral_size"); + + if (bcm_host_get_sdram_address && bcm_host_get_sdram_address()!=0x40000000) { // Pi 2? + info->mem_flg = 0x4; // ARM cannot see VC4 L2 on Pi 2 + info->mem_map = 0x0; + } + + if (bcm_host_get_peripheral_address) info->peri_addr = bcm_host_get_peripheral_address(); + if (bcm_host_get_peripheral_size) info->peri_size = bcm_host_get_peripheral_size(); + + dlclose(handle); + return 0; +} + +unsigned gpu_fft_base_exec_direct ( + struct GPU_FFT_BASE *base, + int num_qpus) { + + unsigned q, t; + + base->peri[V3D_DBCFG] = 0; // Disallow IRQ + base->peri[V3D_DBQITE] = 0; // Disable IRQ + base->peri[V3D_DBQITC] = -1; // Resets IRQ flags + + base->peri[V3D_L2CACTL] = 1<<2; // Clear L2 cache + base->peri[V3D_SLCACTL] = -1; // Clear other caches + + base->peri[V3D_SRQCS] = (1<<7) | (1<<8) | (1<<16); // Reset error bit and counts + + for (q=0; qperi[V3D_SRQUA] = base->vc_unifs[q]; + base->peri[V3D_SRQPC] = base->vc_code; + } + + // Busy wait polling + for (;;) { + if (((base->peri[V3D_SRQCS]>>16) & 0xff) == num_qpus) break; // All done? + } + + return 0; +} + +unsigned gpu_fft_base_exec( + struct GPU_FFT_BASE *base, + int num_qpus) { + + if (base->vc_msg) { + // Use mailbox + // Returns: 0x0 for success; 0x80000000 for timeout + return execute_qpu(base->mb, num_qpus, base->vc_msg, GPU_FFT_NO_FLUSH, GPU_FFT_TIMEOUT); + } + else { + // Direct register poking + return gpu_fft_base_exec_direct(base, num_qpus); + } +} + +int gpu_fft_alloc ( + int mb, + unsigned size, + struct GPU_FFT_PTR *ptr) { + + struct GPU_FFT_HOST host; + struct GPU_FFT_BASE *base; + volatile unsigned *peri; + unsigned handle; + + if (gpu_fft_get_host_info(&host)) return -5; + + if (qpu_enable(mb, 1)) return -1; + + // Shared memory + handle = mem_alloc(mb, size, 4096, host.mem_flg); + if (!handle) { + qpu_enable(mb, 0); + return -3; + } + + peri = (volatile unsigned *) mapmem(host.peri_addr, host.peri_size); + if (!peri) { + mem_free(mb, handle); + qpu_enable(mb, 0); + return -4; + } + + ptr->vc = mem_lock(mb, handle); + ptr->arm.vptr = mapmem(BUS_TO_PHYS(ptr->vc+host.mem_map), size); + + base = (struct GPU_FFT_BASE *) ptr->arm.vptr; + base->peri = peri; + base->peri_size = host.peri_size; + base->mb = mb; + base->handle = handle; + base->size = size; + + return 0; +} + +void gpu_fft_base_release(struct GPU_FFT_BASE *base) { + int mb = base->mb; + unsigned handle = base->handle, size = base->size; + unmapmem((void*)base->peri, base->peri_size); + unmapmem((void*)base, size); + mem_unlock(mb, handle); + mem_free(mb, handle); + qpu_enable(mb, 0); +} + +unsigned gpu_fft_ptr_inc ( + struct GPU_FFT_PTR *ptr, + int bytes) { + + unsigned vc = ptr->vc; + ptr->vc += bytes; + ptr->arm.bptr += bytes; + return vc; +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_shaders.c b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_shaders.c new file mode 100755 index 0000000..f8e3bfe --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_shaders.c @@ -0,0 +1,102 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +static unsigned int shader_256[] = { + #include "hex/shader_256.hex" +}; +static unsigned int shader_512[] = { + #include "hex/shader_512.hex" +}; +static unsigned int shader_1k[] = { + #include "hex/shader_1k.hex" +}; +static unsigned int shader_2k[] = { + #include "hex/shader_2k.hex" +}; +static unsigned int shader_4k[] = { + #include "hex/shader_4k.hex" +}; +static unsigned int shader_8k[] = { + #include "hex/shader_8k.hex" +}; +static unsigned int shader_16k[] = { + #include "hex/shader_16k.hex" +}; +static unsigned int shader_32k[] = { + #include "hex/shader_32k.hex" +}; +static unsigned int shader_64k[] = { + #include "hex/shader_64k.hex" +}; +static unsigned int shader_128k[] = { + #include "hex/shader_128k.hex" +}; +static unsigned int shader_256k[] = { + #include "hex/shader_256k.hex" +}; +static unsigned int shader_512k[] = { + #include "hex/shader_512k.hex" +}; +static unsigned int shader_1024k[] = { + #include "hex/shader_1024k.hex" +}; +static unsigned int shader_2048k[] = { + #include "hex/shader_2048k.hex" +}; +static unsigned int shader_4096k[] = { + #include "hex/shader_4096k.hex" +}; + +static struct { + unsigned int size, *code; +} +shaders[] = { + {sizeof(shader_256), shader_256}, + {sizeof(shader_512), shader_512}, + {sizeof(shader_1k), shader_1k}, + {sizeof(shader_2k), shader_2k}, + {sizeof(shader_4k), shader_4k}, + {sizeof(shader_8k), shader_8k}, + {sizeof(shader_16k), shader_16k}, + {sizeof(shader_32k), shader_32k}, + {sizeof(shader_64k), shader_64k}, + {sizeof(shader_128k), shader_128k}, + {sizeof(shader_256k), shader_256k}, + {sizeof(shader_512k), shader_512k}, + {sizeof(shader_1024k), shader_1024k}, + {sizeof(shader_2048k), shader_2048k}, + {sizeof(shader_4096k), shader_4096k} +}; + +unsigned int gpu_fft_shader_size(int log2_N) { + return shaders[log2_N-8].size; +} + +unsigned int *gpu_fft_shader_code(int log2_N) { + return shaders[log2_N-8].code; +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.c b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.c new file mode 100755 index 0000000..265c2b6 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.c @@ -0,0 +1,95 @@ +/* +BCM2835 "GPU_FFT" release 2.0 +Copyright (c) 2014, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "gpu_fft_trans.h" + +static unsigned int shader_trans[1024] = { + #include "hex/shader_trans.hex" +}; + +int gpu_fft_trans_prepare( + int mb, + struct GPU_FFT *src, + struct GPU_FFT *dst, + struct GPU_FFT_TRANS **out) { + + unsigned size, info_bytes, code_bytes, unif_bytes, mail_bytes; + int ret; + + struct GPU_FFT_TRANS *info; + struct GPU_FFT_BASE *base; + struct GPU_FFT_PTR ptr; + + info_bytes = code_bytes = unif_bytes = mail_bytes = 4096; // 4k align + + size = info_bytes + // control + code_bytes + // shader, aligned + unif_bytes + // uniforms + mail_bytes; // mailbox message + + ret = gpu_fft_alloc(mb, size, &ptr); + if (ret) return ret; + + // Control header + info = (struct GPU_FFT_TRANS *) ptr.arm.vptr; + base = (struct GPU_FFT_BASE *) info; + gpu_fft_ptr_inc(&ptr, info_bytes); + + // Shader code + memcpy(ptr.arm.vptr, shader_trans, code_bytes); + base->vc_code = gpu_fft_ptr_inc(&ptr, code_bytes); + + // Uniforms + ptr.arm.uptr[0] = src->base.vc_msg; + ptr.arm.uptr[1] = ((char*)src->out) - ((char*)src->in); // output buffer offset + ptr.arm.uptr[2] = dst->base.vc_msg; + ptr.arm.uptr[3] = 0; + ptr.arm.uptr[4] = src->step * sizeof(struct GPU_FFT_COMPLEX); + ptr.arm.uptr[5] = dst->step * sizeof(struct GPU_FFT_COMPLEX); + ptr.arm.uptr[6] = src->x < dst->y? src->x : dst->y; + ptr.arm.uptr[7] = src->y < dst->x? src->y : dst->x; + base->vc_unifs[0] = gpu_fft_ptr_inc(&ptr, unif_bytes); + + // Mailbox message + ptr.arm.uptr[0] = base->vc_unifs[0]; + ptr.arm.uptr[1] = base->vc_code; + base->vc_msg = gpu_fft_ptr_inc(&ptr, mail_bytes); + + *out = info; + return 0; +} + +unsigned gpu_fft_trans_execute(struct GPU_FFT_TRANS *info) { + return gpu_fft_base_exec(&info->base, 1); +} + +void gpu_fft_trans_release(struct GPU_FFT_TRANS *info) { + gpu_fft_base_release(&info->base); +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.h b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.h new file mode 100755 index 0000000..682efc1 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.h @@ -0,0 +1,45 @@ +/* +BCM2835 "GPU_FFT" release 2.0 +Copyright (c) 2014, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "gpu_fft.h" + +struct GPU_FFT_TRANS { + struct GPU_FFT_BASE base; +}; + +int gpu_fft_trans_prepare( + int mb, + struct GPU_FFT *src, + struct GPU_FFT *dst, + struct GPU_FFT_TRANS **out); + +unsigned gpu_fft_trans_execute( // src->out ==> T ==> dst->in + struct GPU_FFT_TRANS *info); + +void gpu_fft_trans_release( + struct GPU_FFT_TRANS *info); diff --git a/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_twiddles.c b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_twiddles.c new file mode 100755 index 0000000..5323650 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_twiddles.c @@ -0,0 +1,315 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "gpu_fft.h" + +#define ALPHA(dx) (2*pow(sin((dx)/2),2)) +#define BETA(dx) (sin(dx)) + +static double k[16] = {0,8,4,4,2,2,2,2,1,1,1,1,1,1,1,1}; +static double m[16] = {0,0,0,1,0,1,2,3,0,1,2,3,4,5,6,7}; + +/****************************************************************************/ + +static float *twiddles_base_16(double two_pi, float *out, double theta) { + int i; + for (i=0; i<16; i++) { + *out++ = cos(two_pi/16*k[i]*m[i] + theta*k[i]); + *out++ = sin(two_pi/16*k[i]*m[i] + theta*k[i]); + } + return out; +} + +static float *twiddles_base_32(double two_pi, float *out, double theta) { + int i; + for (i=0; i<16; i++) { + *out++ = cos(two_pi/32*i + theta); + *out++ = sin(two_pi/32*i + theta); + } + return twiddles_base_16(two_pi, out, 2*theta); +} + +static float *twiddles_base_64(double two_pi, float *out) { + int i; + for (i=0; i<32; i++) { + *out++ = cos(two_pi/64*i); + *out++ = sin(two_pi/64*i); + } + return twiddles_base_32(two_pi, out, 0); +} + +/****************************************************************************/ + +static float *twiddles_step_16(double two_pi, float *out, double theta) { + int i; + for (i=0; i<16; i++) { + *out++ = ALPHA(theta*k[i]); + *out++ = BETA(theta*k[i]); + } + return out; +} + +static float *twiddles_step_32(double two_pi, float *out, double theta) { + int i; + for (i=0; i<16; i++) { + *out++ = ALPHA(theta); + *out++ = BETA(theta); + } + return twiddles_step_16(two_pi, out, 2*theta); +} + +static float *twiddles_step_64(double two_pi, float *out, double theta) { + int i; + for (i=0; i<32; i++) { + *out++ = ALPHA(theta); + *out++ = BETA(theta); + } + return twiddles_step_32(two_pi, out, 2*theta); +} + +/****************************************************************************/ + +static void twiddles_256(double two_pi, float *out) { + double N=256; + int q; + + out = twiddles_base_16(two_pi, out, 0); + out = twiddles_step_16(two_pi, out, two_pi/N * GPU_FFT_QPUS); + + for (q=0; q22) return -1; + *shared = shaders[log2_N-8].shared; + *unique = shaders[log2_N-8].unique; + *passes = shaders[log2_N-8].passes; + return 0; +} + +void gpu_fft_twiddle_data(int log2_N, int direction, float *out) { + shaders[log2_N-8].twiddles((direction==GPU_FFT_FWD?-2:2)*GPU_FFT_PI, out); +} diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hello_fft.c b/host_applications/linux/apps/hello_pi/hello_fft/hello_fft.c new file mode 100755 index 0000000..90317a4 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hello_fft.c @@ -0,0 +1,109 @@ +/* +BCM2835 "GPU_FFT" release 3.0 +Copyright (c) 2015, Andrew Holme. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "mailbox.h" +#include "gpu_fft.h" + +char Usage[] = + "Usage: hello_fft.bin log2_N [jobs [loops]]\n" + "log2_N = log2(FFT_length), log2_N = 8...22\n" + "jobs = transforms per batch, jobs>0, default 1\n" + "loops = number of test repeats, loops>0, default 1\n"; + +unsigned Microseconds(void) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ts.tv_sec*1000000 + ts.tv_nsec/1000; +} + +int main(int argc, char *argv[]) { + int i, j, k, ret, loops, freq, log2_N, jobs, N, mb = mbox_open(); + unsigned t[2]; + double tsq[2]; + + struct GPU_FFT_COMPLEX *base; + struct GPU_FFT *fft; + + log2_N = argc>1? atoi(argv[1]) : 12; // 8 <= log2_N <= 22 + jobs = argc>2? atoi(argv[2]) : 1; // transforms per batch + loops = argc>3? atoi(argv[3]) : 1; // test repetitions + + if (argc<2 || jobs<1 || loops<1) { + printf(Usage); + return -1; + } + + N = 1<in + j*fft->step; // input buffer + for (i=0; iout + j*fft->step; // output buffer + freq = j+1; + for (i=0; i +#include +#include + +#include "gpu_fft_trans.h" +#include "hello_fft_2d_bitmap.h" + +#define log2_N 9 +#define N (1<io+(fft)->step*(y)) + +unsigned Microseconds(void) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ts.tv_sec*1000000 + ts.tv_nsec/1000; +} + +int main(int argc, char *argv[]) { + int x, y, ret, mb = mbox_open(); + unsigned t[4]; + + struct GPU_FFT_COMPLEX *row; + struct GPU_FFT_TRANS *trans; + struct GPU_FFT *fft_pass[2]; + + BITMAPFILEHEADER bfh; + BITMAPINFOHEADER bih; + + // Create Windows bitmap file + FILE *fp = fopen("hello_fft_2d.bmp", "wb"); + if (!fp) return -666; + + // Write bitmap header + memset(&bfh, 0, sizeof(bfh)); + bfh.bfType = 0x4D42; //"BM" + bfh.bfSize = N*N*3; + bfh.bfOffBits = sizeof(bfh) + sizeof(bih); + fwrite(&bfh, sizeof(bfh), 1, fp); + + // Write bitmap info + memset(&bih, 0, sizeof(bih)); + bih.biSize = sizeof(bih); + bih.biWidth = N; + bih.biHeight = N; + bih.biPlanes = 1; + bih.biBitCount = 24; + bih.biCompression = BI_RGB; + fwrite(&bih, sizeof(bih), 1, fp); + + // Prepare 1st FFT pass + ret = gpu_fft_prepare(mb, log2_N, GPU_FFT_REV, N, fft_pass+0); + if (ret) { + return ret; + } + // Prepare 2nd FFT pass + ret = gpu_fft_prepare(mb, log2_N, GPU_FFT_REV, N, fft_pass+1); + if (ret) { + gpu_fft_release(fft_pass[0]); + return ret; + } + // Transpose from 1st pass output to 2nd pass input + ret = gpu_fft_trans_prepare(mb, fft_pass[0], fft_pass[1], &trans); + if (ret) { + gpu_fft_release(fft_pass[0]); + gpu_fft_release(fft_pass[1]); + return ret; + } + + // Clear input array + for (y=0; y FFT() ==> T() ==> FFT() ==> + usleep(1); /* yield to OS */ t[0] = Microseconds(); + gpu_fft_execute(fft_pass[0]); t[1] = Microseconds(); + gpu_fft_trans_execute(trans); t[2] = Microseconds(); + gpu_fft_execute(fft_pass[1]); t[3] = Microseconds(); + + // Write output to bmp file + for (y=0; y> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd78, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149c01c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149c01c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9db1c0, 0x10020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119db3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c91c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb50, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00001258, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149c01c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149c01c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9db1c0, 0x10020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119db3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c91c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff998, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff970, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff9d0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00007fff, 0xe0020827, // mov r0, 0x7FFF +0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 +0xfffff9a0, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff710, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff4a0, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff480, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff460, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff440, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff1b0, 0xf00809e7, // brr.allz -, r:pass_3 +0x00000060, 0xe0020827, // mov r0, 3*4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000007, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000006, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xffffef40, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xffffecb0, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xffffed78, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_128k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_128k.hex new file mode 100755 index 0000000..6b82f92 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_128k.hex @@ -0,0 +1,735 @@ +0x00000011, 0xe0021227, // mov rb_STAGES, STAGES +0x00000010, 0xe00216a7, // mov rb_0x10, 0x10 +0x00000040, 0xe00216e7, // mov rb_0x40, 0x40 +0x00000080, 0xe0021727, // mov rb_0x80, 0x80 +0x000000f0, 0xe0021767, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217a7, // mov rb_0x100, 0x100 +0x00000fff, 0xe00217e7, // mov rb_0xFFF, 0xFFF +0x55555555, 0xe0020767, // mov rx_0x55555555, 0x55555555 +0x33333333, 0xe00207a7, // mov rx_0x33333333, 0x33333333 +0x0f0f0f0f, 0xe00207e7, // mov rx_0x0F0F0F0F, 0x0F0F0F0F +0x00ff00ff, 0xe0021627, // mov rx_0x00FF00FF, 0x00FF00FF +0x0000ffff, 0xe0021667, // mov rx_0x0000FFFF, 0x0000FFFF +0x88104000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc000ffc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05bdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000c8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc0007fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05bdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000560, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd78, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d81c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d81c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9da1c0, 0x10020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119da3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9cc1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000d00, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d81c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d81c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9da1c0, 0x10020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119da3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9cc1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa08, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff9e0, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffaf0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x141dfdc0, 0x100229e7, // and.setf -, ra_points, rb_0xFFF +0xfffffac8, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff938, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff778, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffff758, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff5c8, 0xf00809e7, // brr.allz -, r:pass_3 +0x00000020, 0xe0020827, // mov r0, 4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff408, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff278, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff2d0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_16k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_16k.hex new file mode 100755 index 0000000..160d783 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_16k.hex @@ -0,0 +1,688 @@ +0x00000010, 0xe00216e7, // mov rb_0x10, 0x10 +0x00000040, 0xe0021727, // mov rb_0x40, 0x40 +0x00000080, 0xe0021767, // mov rb_0x80, 0x80 +0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 +0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00216a7, // mov rx_0x00FF, 0x00FF +0x88104000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc0001fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000c8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc0000fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x000005f0, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c11c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffbf0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb10, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000b10, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c11c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff9a0, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1cedc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff978, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff988, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff968, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cedc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff6d8, 0xf00809e7, // brr.allz -, r:pass_2 +0x00000020, 0xe0020827, // mov r0, 4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff5f8, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cedc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff468, 0xf00809e7, // brr.allz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff4c0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_1k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_1k.hex new file mode 100755 index 0000000..7de3279 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_1k.hex @@ -0,0 +1,523 @@ +0x00000010, 0xe00216e7, // mov rb_0x10, 0x10 +0x00000040, 0xe0021727, // mov rb_0x40, 0x40 +0x000000f0, 0xe0021767, // mov rb_0xF0, 0xF0 +0x00005555, 0xe00207a7, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00217a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00217e7, // mov rx_0x00FF, 0x00FF +0x90104000, 0xe0020767, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202a7, // mov rx_tw_shared, unif +0x15827d80, 0x100212a7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x100246e0, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10024720, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000c8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15767d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc00000c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000588, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c31c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x956c2ff6, 0x100246c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95707ff6, 0x10024707, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95741ff6, 0x10024741, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffbf0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x956c2ff6, 0x100246c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95707ff6, 0x10024707, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95741ff6, 0x10024741, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x000007a0, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c31c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x0e1cadc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff9e8, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100202e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100212e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff9f8, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x95492dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024a7c80, 0x10020827, // fsub r0, a, b +0x024a7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024a7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014e7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024a7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204e7, // fadd a+1, r0, r1 +0x029d2ec0, 0x10020827, // fsub r0, a, b +0x029d21c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d2e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d33c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d2e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214e7, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x202e7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cb017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cb01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x212e709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02427c80, 0x10020827, // fsub r0, a, b +0x02427180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02427c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01467380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02427c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020467, // fadd a+1, r0, r1 +0x029d0ec0, 0x10020827, // fsub r0, a, b +0x029d01c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d0e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d13c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d0e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021467, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cadc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff768, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff830, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2048k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2048k.hex new file mode 100755 index 0000000..c49cd94 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2048k.hex @@ -0,0 +1,1353 @@ +0x00000010, 0xe0021227, // mov rb_0x10, 0x10 +0x000001d0, 0xe0021967, // mov r5rep, 0x1D0 +0x15827d80, 0x100203e7, // mov rx_tw_shared, unif +0x15827d80, 0x100213e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000002e8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x153a7d80, 0x10020827, // mov r0, ra_vdw_32 +0x8c04ddf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00080000, 0xe00208e7, // mov r3, PASS32_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000520, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x80904000, 0xe0020827, // mov r0, vdw_setup_0(1, 16, dma_h32(0,0)) +0x00000040, 0xe0020867, // mov r1, 0x40 +0x8c067c76, 0x10024061, // add ra_save_ptr, ra_save_ptr, r1; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00040000, 0xe00208e7, // mov r3, PASS64_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000002b8, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000e0, 0xf0f809e7, // brr -, r:2f +0x00000010, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000c0, 0xf0f809e7, // brr -, r:2f +0x00000011, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000a0, 0xf0f809e7, // brr -, r:2f +0x00000012, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000080, 0xf0f809e7, // brr -, r:2f +0x00000013, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000060, 0xf0f809e7, // brr -, r:2f +0x00000014, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000040, 0xf0f809e7, // brr -, r:2f +0x00000015, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000020, 0xf0f809e7, // brr -, r:2f +0x00000016, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f809e7, // brr -, r:2f +0x00000017, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000008, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000009, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000a, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000b, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000c, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000d, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000e, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000f, 0xe80009e7, // mov -, srel(i+8) +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000998, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd50, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffbe0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 +0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 +0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 +0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 +0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffa30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffff8c0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 +0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 +0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 +0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 +0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 +0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 +0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 +0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 +0x2225a19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 +0x206a701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 +0x00000000, 0xf0f549e7, // bra -, ra_save_64 +0x209da017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 +0x216897d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 +0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re +0x8c14cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff7e0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff790, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x9538eff6, 0x1002438e, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00001378, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100206a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100216a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c61c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c81c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff4f8, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000015, 0xe0020867, // mov r1, STAGES +0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 +0xfffff4c8, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm +0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 +0x80904000, 0xe00203a7, // mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) +0x80905000, 0xe00213a7, // mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) +0x00000015, 0xe00212e7, // mov rb_STAGES, STAGES +0x000000f0, 0xe0021327, // mov rb_0xF0, 0xF0 +0x00000040, 0xe0021367, // mov rb_0x40, 0x40 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff8b0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00007fff, 0xe0020827, // mov r0, 0x7FFF +0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 +0xfffff880, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100601e7, // add.ifnz ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff5f0, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000007, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000006, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff380, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff360, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff340, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff320, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0x00000100, 0xe0020827, // mov r0, 0x100 +0xfffff088, 0xf00809e7, // brr.allz -, r:pass_3 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000060, 0xe0020827, // mov r0, (4-1)*4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000009, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000008, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xffffee18, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xffffeb88, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xffffec58, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256.hex new file mode 100755 index 0000000..bfd5b45 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256.hex @@ -0,0 +1,359 @@ +0x00000040, 0xe00217a7, // mov rb_0x40, 0x40 +0x00000080, 0xe00217e7, // mov rb_0x80, 0x80 +0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x88104000, 0xe0020727, // mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88104800, 0xe0021727, // mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) +0x15827d80, 0x10020227, // mov rx_tw_shared, unif +0x15827d80, 0x10021227, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x100246e0, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100256e0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100049e0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100009e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc0000040, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05edf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x156e7d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000248, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffd50, 0xf0f80027, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0xfffffd30, 0xf0f80027, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9c8e00, 0x10020e27, // add t0s, ptr, r0 +0x0c9c8e40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c227c00, 0x10020e27, // add t0s, ptr, r0 +0x0c227c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020267, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021267, // mov rb_tw_im+dst, r4 +0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb50, 0xf0f80027, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20267016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209c9017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209c901f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2126709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02367c80, 0x10020827, // fsub r0, a, b +0x02367180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02367c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x013a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002588d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02367c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100203a7, // fadd a+1, r0, r1 +0x029cdec0, 0x10020827, // fsub r0, a, b +0x029cd1c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029cde40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019ce3c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002488d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029cde80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100213a7, // fadd a+1, r0, r1 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0xfffff9c8, 0xf0f80027, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x956dbff6, 0x100246db, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x9571cff6, 0x1002471c, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0xfffff9d0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256k.hex new file mode 100755 index 0000000..d51e651 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256k.hex @@ -0,0 +1,861 @@ +0x00000012, 0xe0021227, // mov rb_STAGES, STAGES +0x00000010, 0xe00216a7, // mov rb_0x10, 0x10 +0x00000040, 0xe00216e7, // mov rb_0x40, 0x40 +0x00000080, 0xe0021727, // mov rb_0x80, 0x80 +0x000000f0, 0xe0021767, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217a7, // mov rb_0x100, 0x100 +0x00001fff, 0xe00217e7, // mov rb_0x1FFF, 0x1FFF +0x55555555, 0xe0020767, // mov rx_0x55555555, 0x55555555 +0x33333333, 0xe00207a7, // mov rx_0x33333333, 0x33333333 +0x0f0f0f0f, 0xe00207e7, // mov rx_0x0F0F0F0F, 0x0F0F0F0F +0x00ff00ff, 0xe0021627, // mov rx_0x00FF00FF, 0x00FF00FF +0x0000ffff, 0xe0021667, // mov rx_0x0000FFFF, 0x0000FFFF +0x80904000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0( 1, 16, dma_h32( 0,0)) +0x80905000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0( 1, 16, dma_h32(32,0)) +0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000001d0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x156e7d80, 0x10020827, // mov r0, arg_vdw +0x8c05bdf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00020000, 0xe00208e7, // mov r3, PASS16_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000c8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc000ffc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05bdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000640, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd78, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d81c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d81c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9da1c0, 0x10020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119da3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9cb1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb38, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffae8, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000ef0, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d81c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d81c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9da1c0, 0x10020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119da3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9cb1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff928, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff900, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x141dfdc0, 0x100229e7, // and.setf -, ra_points, rb_0x1FFF +0xfffff9e8, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff858, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff698, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffff678, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffff658, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffff638, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff4a8, 0xf00809e7, // brr.allz -, r:pass_3 +0x00000060, 0xe0020827, // mov r0, 3*4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dcdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15adf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff2a0, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff010, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff0e0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2k.hex new file mode 100755 index 0000000..bd30abb --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2k.hex @@ -0,0 +1,765 @@ +0x00000010, 0xe0021727, // mov rb_0x10, 0x10 +0x00000040, 0xe0021767, // mov rb_0x40, 0x40 +0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 +0x000001d0, 0xe00217e7, // mov rb_0x1D0, 0x1D0 +0x00005555, 0xe0020727, // mov rx_0x5555, 0x5555 +0x00003333, 0xe0020767, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207a7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00207e7, // mov rx_0x00FF, 0x00FF +0x15827d80, 0x100203e7, // mov rx_tw_shared, unif +0x15827d80, 0x100213e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000c8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15367d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc00001c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000f8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0xa0104000, 0xe0021c67, // mov vw_setup, vdw_setup_0(64, 16, dma_h32(0,0)) +0xc00000c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(PASS64_STRIDE-16*4) +0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, step; mov vw_addr, ra_save_ptr +0x000002b8, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000e0, 0xf0f809e7, // brr -, r:2f +0x00000010, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000c0, 0xf0f809e7, // brr -, r:2f +0x00000011, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000a0, 0xf0f809e7, // brr -, r:2f +0x00000012, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000080, 0xf0f809e7, // brr -, r:2f +0x00000013, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000060, 0xf0f809e7, // brr -, r:2f +0x00000014, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000040, 0xf0f809e7, // brr -, r:2f +0x00000015, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000020, 0xf0f809e7, // brr -, r:2f +0x00000016, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f809e7, // brr -, r:2f +0x00000017, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000008, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000009, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000a, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000b, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000c, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000d, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000e, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000f, 0xe80009e7, // mov -, srel(i+8) +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000858, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 +0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 +0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 +0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffb20, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffa00, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 +0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 +0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 +0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 +0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 +0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 +0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 +0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 +0x2225a19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 +0x206a701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 +0x00000000, 0xf0f549e7, // bra -, ra_save_64 +0x209da017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 +0x216897d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 +0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff920, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff8d0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x9534dff6, 0x1002434d, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000870, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100206a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100216a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c61c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c21c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff688, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x0e1cbdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff660, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm +0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 +0x90104000, 0xe0020367, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021367, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff920, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff690, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff760, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_32k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_32k.hex new file mode 100755 index 0000000..3b6fd77 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_32k.hex @@ -0,0 +1,697 @@ +0x00000010, 0xe00216e7, // mov rb_0x10, 0x10 +0x00000040, 0xe0021727, // mov rb_0x40, 0x40 +0x00000080, 0xe0021767, // mov rb_0x80, 0x80 +0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 +0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00216a7, // mov rx_0x00FF, 0x00FF +0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202a7, // mov rx_tw_shared, unif +0x15827d80, 0x100212a7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x100246a0, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246e0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000c8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc0001fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156e7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000588, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c21c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffbf0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204a7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d200f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204a700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22092c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x95682ff6, 0x10024682, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c7ff6, 0x100246c7, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000d00, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c21c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1cfdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff9e8, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100202e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100212e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff9f8, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff9d8, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff9b8, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff998, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x95492dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024a7c80, 0x10020827, // fsub r0, a, b +0x024a7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024a7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014e7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024a7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204e7, // fadd a+1, r0, r1 +0x029d2ec0, 0x10020827, // fsub r0, a, b +0x029d21c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d2e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d33c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d2e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214e7, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x202e7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cb017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cb01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x212e709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02427c80, 0x10020827, // fsub r0, a, b +0x02427180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02427c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01467380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02427c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020467, // fadd a+1, r0, r1 +0x029d0ec0, 0x10020827, // fsub r0, a, b +0x029d01c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d0e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d13c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d0e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021467, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cfdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff708, 0xf00809e7, // brr.allz -, r:pass_2 +0x00000060, 0xe0020827, // mov r0, 3*4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cae00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cae40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214a7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100202e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100212e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2a7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2a7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024451, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe00244d3, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff498, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x95492dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024a7c80, 0x10020827, // fsub r0, a, b +0x024a7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024a7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014e7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024a7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204e7, // fadd a+1, r0, r1 +0x029d2ec0, 0x10020827, // fsub r0, a, b +0x029d21c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d2e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d33c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024892, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d2e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214e7, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x202e7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cb017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cb01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x212e709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02427c80, 0x10020827, // fsub r0, a, b +0x02427180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02427c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01467380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02427c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020467, // fadd a+1, r0, r1 +0x029d0ec0, 0x10020827, // fsub r0, a, b +0x029d01c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d0e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d13c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024890, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d0e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021467, // fadd a+1, r0, r1 +0x95410dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cfdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff208, 0xf00809e7, // brr.allz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff2d0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4096k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4096k.hex new file mode 100755 index 0000000..f49df21 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4096k.hex @@ -0,0 +1,1523 @@ +0x00000010, 0xe0021227, // mov rb_0x10, 0x10 +0x000001d0, 0xe0021967, // mov r5rep, 0x1D0 +0x15827d80, 0x100203e7, // mov rx_tw_shared, unif +0x15827d80, 0x100213e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000002e8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x153a7d80, 0x10020827, // mov r0, ra_vdw_32 +0x8c04ddf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00100000, 0xe00208e7, // mov r3, PASS32_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000520, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x80904000, 0xe0020827, // mov r0, vdw_setup_0(1, 16, dma_h32(0,0)) +0x00000040, 0xe0020867, // mov r1, 0x40 +0x8c067c76, 0x10024061, // add ra_save_ptr, ra_save_ptr, r1; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00080000, 0xe00208e7, // mov r3, PASS64_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000002b8, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000e0, 0xf0f809e7, // brr -, r:2f +0x00000010, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000c0, 0xf0f809e7, // brr -, r:2f +0x00000011, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000a0, 0xf0f809e7, // brr -, r:2f +0x00000012, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000080, 0xf0f809e7, // brr -, r:2f +0x00000013, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000060, 0xf0f809e7, // brr -, r:2f +0x00000014, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000040, 0xf0f809e7, // brr -, r:2f +0x00000015, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000020, 0xf0f809e7, // brr -, r:2f +0x00000016, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f809e7, // brr -, r:2f +0x00000017, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000008, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000009, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000a, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000b, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000c, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000d, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000e, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000f, 0xe80009e7, // mov -, srel(i+8) +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000ba8, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd50, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffbe0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 +0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 +0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 +0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 +0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffa30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffff8c0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 +0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 +0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 +0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 +0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 +0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 +0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 +0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 +0x2225b19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 +0x206e701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 +0x00000000, 0xf0f549e7, // bra -, ra_save_64 +0x209db017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 +0x216c97d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 +0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff7e0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff790, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 +0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 +0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 +0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 +0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff700, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff6b0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 +0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 +0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 +0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 +0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 +0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 +0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 +0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 +0x2225b19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 +0x206e701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 +0x00000000, 0xf0f549e7, // bra -, ra_save_64 +0x209db017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 +0x216c97d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 +0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re +0x8c14cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff5d0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff580, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x9538eff6, 0x1002438e, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x000016b8, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100206e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100216e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c61c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x55555555, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x33333333, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0f0f0f0f, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x00ff00ff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0000ffff, 0xe00208a7, // mov r2, mask +0x149e7080, 0x10020867, // and r1, r0, r2 +0x0e9c81c0, 0x10020827, // shr r0, r0, shift +0x149e7080, 0x10020827, // and r0, r0, r2 +0x119c83c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c71c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff2e8, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000016, 0xe0020867, // mov r1, STAGES +0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 +0xfffff2b8, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100206e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100216e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe002469a, // mov ra_tw_re+TW48+1, 0; mov rb_tw_im+TW48+1, 0 +0x00000000, 0xe002471c, // mov ra_tw_re+TW64+1, 0; mov rb_tw_im+TW64+1, 0 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000007, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000006, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020767, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021767, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100207a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100217a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c61c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff568, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x0000ffff, 0xe0020827, // mov r0, 0xFFFF +0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 +0xfffff538, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100601e7, // add.ifnz ra_points, ra_points, r0 +0x956dbdbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x207a7016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209de017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209de01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x217a709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x026e7c80, 0x10020827, // fsub r0, a, b +0x026e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x026e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01727380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002589b, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x026e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020727, // fadd a+1, r0, r1 +0x029dbec0, 0x10020827, // fsub r0, a, b +0x029db1c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029dbe40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019dc3c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002489b, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029dbe80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021727, // fadd a+1, r0, r1 +0x95659dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20767016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209dd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209dd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2176709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02667c80, 0x10020827, // fsub r0, a, b +0x02667180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02667c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x016a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025899, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02667c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100206a7, // fadd a+1, r0, r1 +0x029d9ec0, 0x10020827, // fsub r0, a, b +0x029d91c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d9e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019da3c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024899, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d9e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100216a7, // fadd a+1, r0, r1 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x00000016, 0xe0020867, // mov r1, STAGES +0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 +0xfffff0a0, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm +0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 +0x80904000, 0xe00203a7, // mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) +0x80905000, 0xe00213a7, // mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) +0x00000016, 0xe00212e7, // mov rb_STAGES, STAGES +0x000000f0, 0xe0021327, // mov rb_0xF0, 0xF0 +0x00000040, 0xe0021367, // mov rb_0x40, 0x40 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000009, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000008, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff008, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x000003ff, 0xe0020827, // mov r0, 0x3FF +0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 +0xffffefd8, 0xf01809e7, // brr.allnz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100601e7, // add.ifnz ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xffffed48, 0xf00809e7, // brr.allz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x0000000b, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x0000000a, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xffffead8, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cbdc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xffffe848, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xffffe918, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4k.hex new file mode 100755 index 0000000..c37e50d --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4k.hex @@ -0,0 +1,514 @@ +0x00000020, 0xe0021767, // mov rb_0x20, 0x20 +0x00000040, 0xe00217a7, // mov rb_0x40, 0x40 +0x00000080, 0xe00217e7, // mov rb_0x80, 0x80 +0x00005555, 0xe0020727, // mov rx_0x5555, 0x5555 +0x00003333, 0xe0020767, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207a7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00207e7, // mov rx_0x00FF, 0x00FF +0x88104000, 0xe00206e7, // mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88104800, 0xe00216e7, // mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) +0x15827d80, 0x10020227, // mov rx_tw_shared, unif +0x15827d80, 0x10021227, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x100246a0, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100256a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100049e0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100009e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc00007c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05edf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x156a7d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x000003e8, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f409e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x000000cc, 0xe20229e7, // mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] +0x959fa000, 0xd002c8a0, // mov r2, r0; mov.ifnz r0, r0 << 6 +0x959fa249, 0xd002c8e1, // mov r3, r1; mov.ifnz r1, r1 << 6 +0x00003300, 0xe20229e7, // mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] +0x809f6012, 0xd000c9e0, // nop; mov.ifnz r0, r2 >> 6 +0x809f601b, 0xd000c9e1, // nop; mov.ifnz r1, r3 >> 6 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x000000cc, 0xe20229e7, // mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] +0x959fa000, 0xd002c8a0, // mov r2, r0; mov.ifnz r0, r0 << 6 +0x959fa249, 0xd002c8e1, // mov r3, r1; mov.ifnz r1, r1 << 6 +0x00003300, 0xe20229e7, // mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] +0x809f6012, 0xd000c9e0, // nop; mov.ifnz r0, r2 >> 6 +0x809f601b, 0xd000c9e1, // nop; mov.ifnz r1, r3 >> 6 +0xfffffd40, 0xf0f809e7, // brr -, r:fft_16 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffcf8, 0xf0f809e7, // brr -, r:fft_16 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000928, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c227c00, 0x10020e27, // add t0s, ptr, r0 +0x0c227c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c11c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x000000cc, 0xe20229e7, // mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] +0x959fa000, 0xd002c8a0, // mov r2, r0; mov.ifnz r0, r0 << 6 +0x959fa249, 0xd002c8e1, // mov r3, r1; mov.ifnz r1, r1 << 6 +0x00003300, 0xe20229e7, // mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] +0x809f6012, 0xd000c9e0, // nop; mov.ifnz r0, r2 >> 6 +0x809f601b, 0xd000c9e1, // nop; mov.ifnz r1, r3 >> 6 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffbe0, 0xf0f80027, // brr ra_link_1, r:pass_1 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x0e1ccdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffffbb8, 0xf00809e7, // brr.allz -, r:pass_1 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c227c00, 0x10020e27, // add t0s, ptr, r0 +0x0c227c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c227c00, 0x10020e27, // add t0s, ptr, r0 +0x0c227c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020267, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021267, // mov rb_tw_im+dst, r4 +0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb78, 0xf0f80027, // brr ra_link_1, r:pass_2 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffffb58, 0xf0f80027, // brr ra_link_1, r:pass_2 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x0d01ddc0, 0x10020027, // sub ra_link_1, ra_link_1, rb_0x20 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20267016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209c9017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209c901f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2126709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02367c80, 0x10020827, // fsub r0, a, b +0x02367180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02367c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x013a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002588d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02367c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100203a7, // fadd a+1, r0, r1 +0x029cdec0, 0x10020827, // fsub r0, a, b +0x029cd1c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029cde40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019ce3c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002488d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029cde80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100213a7, // fadd a+1, r0, r1 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1ccdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff9c0, 0xf00809e7, // brr.allz -, r:pass_2 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9c8e00, 0x10020e27, // add t0s, ptr, r0 +0x0c9c8e40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c227c00, 0x10020e27, // add t0s, ptr, r0 +0x0c227c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020267, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021267, // mov rb_tw_im+dst, r4 +0x00000000, 0xe002438e, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15fdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff808, 0xf0f80027, // brr ra_link_1, r:pass_3 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20267016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209c9017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209c901f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2126709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02367c80, 0x10020827, // fsub r0, a, b +0x02367180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02367c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x013a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002588d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02367c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100203a7, // fadd a+1, r0, r1 +0x029cdec0, 0x10020827, // fsub r0, a, b +0x029cd1c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029cde40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019ce3c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x1002488d, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029cde80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100213a7, // fadd a+1, r0, r1 +0x9534ddbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c362, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d363, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c322, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d323, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c2e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d2e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c2a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d2a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1ccdc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff678, 0xf00809e7, // brr.allz -, r:pass_3 +0x9569aff6, 0x1002469a, // mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm +0x956dbff6, 0x100246db, // mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c027, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff6a8, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512.hex new file mode 100755 index 0000000..505ab41 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512.hex @@ -0,0 +1,494 @@ +0x00000010, 0xe0021727, // mov rb_0x10, 0x10 +0x00000040, 0xe0021767, // mov rb_0x40, 0x40 +0x00000080, 0xe00217a7, // mov rb_0x80, 0x80 +0x000000f0, 0xe00217e7, // mov rb_0xF0, 0xF0 +0x00005555, 0xe0020727, // mov rx_0x5555, 0x5555 +0x00003333, 0xe0020767, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207a7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00207e7, // mov rx_0x00FF, 0x00FF +0x88104000, 0xe00206a7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +0x90104000, 0xe00206e7, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024620, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10024660, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x156a7d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc00000c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000c8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x156e7d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc0000040, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15627d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000510, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c41c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95602ff6, 0x10024602, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95647ff6, 0x10024647, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x956c1ff6, 0x100246c1, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffbf0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95602ff6, 0x10024602, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95680ff6, 0x10024680, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x000005e8, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14727180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14727180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9c41c0, 0xd0020827, // shr r0, r0, 13-STAGES +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa80, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0xfffffa60, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb20, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c9dc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff990, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dedc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff9e8, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512k.hex new file mode 100755 index 0000000..ebc84d8 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512k.hex @@ -0,0 +1,983 @@ +0x00000013, 0xe0021227, // mov rb_STAGES, STAGES +0x00000010, 0xe00216e7, // mov rb_0x10, 0x10 +0x00000040, 0xe0021727, // mov rb_0x40, 0x40 +0x00000080, 0xe0021767, // mov rb_0x80, 0x80 +0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 +0x55555555, 0xe0020767, // mov rx_0x55555555, 0x55555555 +0x33333333, 0xe00207a7, // mov rx_0x33333333, 0x33333333 +0x0f0f0f0f, 0xe00207e7, // mov rx_0x0F0F0F0F, 0x0F0F0F0F +0x00ff00ff, 0xe0021667, // mov rx_0x00FF00FF, 0x00FF00FF +0x0000ffff, 0xe00216a7, // mov rx_0x0000FFFF, 0x0000FFFF +0x80904000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(1, 16, dma_h32( 0,0)) +0x80905000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(1, 16, dma_h32(32,0)) +0x80904000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) +0x80905000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000001d0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x156e7d80, 0x10020827, // mov r0, arg_vdw +0x8c05cdf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00040000, 0xe00208e7, // mov r3, PASS16_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000002e8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x15727d80, 0x10020827, // mov r0, ra_vdw_32 +0x8c05cdf6, 0x10024061, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr +0x00000080, 0xe00208a7, // mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) +0x00020000, 0xe00208e7, // mov r3, PASS32_STRIDE +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x8c9e7080, 0x10024831, // add r0, r0, r2; mov vw_setup, r0 +0x8c9e72c9, 0x10024872, // add r1, r1, r3; mov vw_addr, r1 +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000640, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffd78, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9db1c0, 0x10020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119db3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9ca1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc30, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffba0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x8c15edf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb38, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffae8, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x000010a8, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149d91c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149d91c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9db1c0, 0x10020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119db3c0, 0x10020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0e9ca1c0, 0xd0020827, // shr r0, r0, 32-STAGES-3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff928, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff900, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa10, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00003fff, 0xe0020827, // mov r0, 0x3FFF +0x141e7c00, 0x100229e7, // and.setf -, ra_points, r0 +0xfffff9e0, 0xf01809e7, // brr.allnz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100601e7, // add.ifnz ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff850, 0xf00809e7, // brr.allz -, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff648, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff628, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff608, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0xfffff5e8, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff358, 0xf00809e7, // brr.allz -, r:pass_3 +0x00000060, 0xe0020827, // mov r0, 3*4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000006, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020367, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021367, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff0e8, 0xf0f80227, // brr ra_link_1, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x954d3dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20367016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cd017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cd01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2136709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x024e7c80, 0x10020827, // fsub r0, a, b +0x024e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x024e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01527380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x024e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020527, // fadd a+1, r0, r1 +0x029d3ec0, 0x10020827, // fsub r0, a, b +0x029d31c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d3e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d43c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024893, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d3e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021527, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xffffee58, 0xf00809e7, // brr.allz -, r:pass_4 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xffffef28, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_64k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_64k.hex new file mode 100755 index 0000000..5daa0a5 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_64k.hex @@ -0,0 +1,940 @@ +0x00000010, 0xe0021227, // mov rb_0x10, 0x10 +0x000001d0, 0xe0021967, // mov r5rep, 0x1D0 +0x00005555, 0xe00207a7, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00217a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00217e7, // mov rx_0x00FF, 0x00FF +0x15827d80, 0x100203e7, // mov rx_tw_shared, unif +0x15827d80, 0x100213e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10025020, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x10025060, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000c8, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x153a7d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc0003fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c04ddf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x15327d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x152e7d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000100, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000040, 0xe0020827, // mov r0, 0x40 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0xa0104000, 0xe0021c67, // mov vw_setup, vdw_setup_0(64, 16, dma_h32(0,0)) +0xc0001fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(PASS64_STRIDE-16*4) +0x8c067c36, 0x10024072, // add ra_save_ptr, ra_save_ptr, step; mov vw_addr, ra_save_ptr +0x000002b8, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd00200a7, // shl ra_temp, r0, 5 +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000e0, 0xf0f809e7, // brr -, r:2f +0x00000010, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000c0, 0xf0f809e7, // brr -, r:2f +0x00000011, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x000000a0, 0xf0f809e7, // brr -, r:2f +0x00000012, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000080, 0xf0f809e7, // brr -, r:2f +0x00000013, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000060, 0xf0f809e7, // brr -, r:2f +0x00000014, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000040, 0xf0f809e7, // brr -, r:2f +0x00000015, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000020, 0xf0f809e7, // brr -, r:2f +0x00000016, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f809e7, // brr -, r:2f +0x00000017, 0xe80009e7, // mov -, sacq(i) +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c0fc0, 0x10021c67, // mov vw_setup, rb_vpm +0x012cbdc0, 0x10020c27, // fadd vpm, ra_64+0, rb_64+0 +0x0130cdc0, 0x10020c27, // fadd vpm, ra_64+1, rb_64+1 +0x159c1fc0, 0x10021c67, // mov vw_setup, rb_vpm_16 +0x0134ddc0, 0x10020c27, // fadd vpm, ra_64+2, rb_64+2 +0x0138edc0, 0x10020c27, // fadd vpm, ra_64+3, rb_64+3 +0x159c2fc0, 0x10021c67, // mov vw_setup, rb_vpm_32 +0x022cbdc0, 0x10020c27, // fsub vpm, ra_64+0, rb_64+0 +0x0230cdc0, 0x10020c27, // fsub vpm, ra_64+1, rb_64+1 +0x159c7fc0, 0x10021c67, // mov vw_setup, rb_vpm_48 +0x0234ddc0, 0x10020c27, // fsub vpm, ra_64+2, rb_64+2 +0x0238edc0, 0x10020c27, // fsub vpm, ra_64+3, rb_64+3 +0x00000000, 0xf0fc49e7, // brr -, ra_temp +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000008, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000009, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000a, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000b, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000c, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000d, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000e, 0xe80009e7, // mov -, srel(i+8) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x159c0fc0, 0x10020c67, // mov vr_setup, rb_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x0000000f, 0xe80009e7, // mov -, srel(i+8) +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000858, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda0, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc80, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x01267c00, 0x100202e7, // fadd ra_64+0, ra_32_re, r0 +0x019c9e40, 0x10020327, // fadd ra_64+1, rb_32_im, r1 +0x02267c00, 0x10020367, // fsub ra_64+2, ra_32_re, r0 +0x029c9e40, 0x100203a7, // fsub ra_64+3, rb_32_im, r1 +0x8c167d76, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffb20, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffa00, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x029c9e40, 0x100208e7, // fsub r3, rb_32_im, r1 +0x02267c00, 0x100208a7, // fsub r2, ra_32_re, r0 +0x019c9e40, 0x10020867, // fadd r1, rb_32_im, r1 +0x01267c00, 0x10020827, // fadd r0, ra_32_re, r0 +0x2066700e, 0x100049c9, // nop; fmul rb_32_im, r1, ra_tw_re+TW48 +0x209d900f, 0x100059c9, // nop; fmul ra_32_re, r1, rb_tw_im+TW48 +0x209d9007, 0x100049e1, // nop; fmul r1, r0, rb_tw_im+TW48 +0x216493c6, 0x10025320, // fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 +0x2225a19f, 0x100252c9, // fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 +0x206a701e, 0x100049c9, // nop; fmul rb_32_im, r3, ra_tw_re+TW64 +0x00000000, 0xf0f549e7, // bra -, ra_save_64 +0x209da017, 0x100049e3, // nop; fmul r3, r2, rb_tw_im+TW64 +0x216897d6, 0x100253a2, // fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 +0x02267580, 0x10021367, // fsub rb_64+2, r2, ra_32_re +0x8c14cdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff920, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff8d0, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x205e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d700f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x205e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22097c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f489e7, // bra -, ra_save_32 +0x952c2ff6, 0x100242c2, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95307ff6, 0x10024307, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x9538eff6, 0x1002438e, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_32, rx_save_slave_32 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_64, rx_save_slave_64 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000df0, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020667, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021667, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100206a7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100216a7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c61c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149de1c0, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x149de1c0, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149df1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149df1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x119c31c0, 0xd0020827, // shl r0, r0, STAGES-13 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff688, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000010, 0xe0020867, // mov r1, STAGES +0x0e1e7c40, 0x100229e7, // shr.setf -, ra_points, r1 +0xfffff658, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x00000200, 0xe0020827, // mov r0, 0x200 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159c0fc0, 0x100202e7, // mov ra_vpm_lo, rb_vpm +0x159c1fc0, 0x10020327, // mov ra_vpm_hi, rb_vpm_16 +0x90104000, 0xe00203a7, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe00213a7, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x00000060, 0xe00212e7, // mov rb_3x4x8, 3*4*8 +0x000000f0, 0xe0021327, // mov rb_0xF0, 0xF0 +0x00000040, 0xe0021367, // mov rb_0x40, 0x40 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000005, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000004, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff900, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff8e0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff8c0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0xfffff8a0, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff610, 0xf00809e7, // brr.allz -, r:pass_2 +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x0d20bdc0, 0x10020227, // sub ra_link_1, ra_link_1, rb_3x4x8 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020567, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021567, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cfe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cfe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100205e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100215e7, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000007, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020427, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021427, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000006, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c3e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c3e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024596, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024618, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c148df6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff3a0, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x955d7dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20467016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d1017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d101f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2146709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x025e7c80, 0x10020827, // fsub r0, a, b +0x025e7180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x025e7c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x01627380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x025e7c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10020627, // fadd a+1, r0, r1 +0x029d7ec0, 0x10020827, // fsub r0, a, b +0x029d71c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d7e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d83c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024897, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d7e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x10021627, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20427016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209d0017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209d001f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2142709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02567c80, 0x10020827, // fsub r0, a, b +0x02567180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02567c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x015a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02567c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100205a7, // fadd a+1, r0, r1 +0x029d5ec0, 0x10020827, // fsub r0, a, b +0x029d51c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d5e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d63c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024895, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d5e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100215a7, // fadd a+1, r0, r1 +0x95555dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c562, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d563, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c522, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d523, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c4e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d4e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c4a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d4a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1c8dc0, 0x100229e7, // shr.setf -, ra_points, rb_STAGES +0xfffff110, 0xf00809e7, // brr.allz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x00000100, 0xe0020827, // mov r0, 0x100 +0x0c1e7c00, 0x100201e7, // add ra_points, ra_points, r0 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff1e0, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_8k.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_8k.hex new file mode 100755 index 0000000..7e1f112 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_8k.hex @@ -0,0 +1,603 @@ +0x00000010, 0xe00216e7, // mov rb_0x10, 0x10 +0x00000040, 0xe0021727, // mov rb_0x40, 0x40 +0x00000080, 0xe0021767, // mov rb_0x80, 0x80 +0x000000f0, 0xe00217a7, // mov rb_0xF0, 0xF0 +0x00000100, 0xe00217e7, // mov rb_0x100, 0x100 +0x00005555, 0xe0020767, // mov rx_0x5555, 0x5555 +0x00003333, 0xe00207a7, // mov rx_0x3333, 0x3333 +0x00000f0f, 0xe00207e7, // mov rx_0x0F0F, 0x0F0F +0x000000ff, 0xe00216a7, // mov rx_0x00FF, 0x00FF +0x88104000, 0xe00206e7, // mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +0x88105000, 0xe0021027, // mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +0x90104000, 0xe0020727, // mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +0x90105000, 0xe0021067, // mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) +0x15827d80, 0x100202e7, // mov rx_tw_shared, unif +0x15827d80, 0x100212e7, // mov rx_tw_unique, unif +0x15827d80, 0x10021167, // mov rb_inst, unif +0x00101200, 0xe0020827, // mov r0, vpm_setup(1, 1, v32( 0,0)) +0x00000010, 0xe0020867, // mov r1, vpm_setup(1, 1, v32(16,0)) - vpm_setup(1, 1, v32(0,0)) +0x00000002, 0xe00208a7, // mov r2, vpm_setup(1, 1, v32( 0,2)) - vpm_setup(1, 1, v32(0,0)) +0x409c5017, 0x100049e2, // nop; mul24 r2, r2, in_inst +0xcc9e7081, 0x10024660, // add out_0, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100246a0, // add out_1, r0, r2; v8adds r0, r0, r1 +0xcc9e7081, 0x100250a0, // add out_2, r0, r2; v8adds r0, r0, r1 +0x0c9e7080, 0x100211e7, // add out_3, r0, r2 +0x000000b0, 0xf0f80127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x156e7d80, 0x10021c67, // mov vw_setup, arg_vdw +0xc0000fc0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS16_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000038, 0xf0f81127, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, arg +0x159e7000, 0x10020c27, // mov vpm, r0 +0x159e7240, 0x10020c27, // mov vpm, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, arg_vpm +0x15c27d80, 0x100009e7, // mov -, vpm +0x000000c8, 0xf0f802a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x15727d80, 0x10021c67, // mov vw_setup, ra_vdw_32 +0xc00007c0, 0xe0021c67, // mov vw_setup, vdw_setup_1(0) + PASS32_STRIDE-16*4 +0x8c05cdf6, 0x10024072, // add ra_save_ptr, ra_save_ptr, rb_0x40; mov vw_addr, ra_save_ptr +0x00000050, 0xf0f812a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10021c67, // mov vw_setup, ra_vpm_lo +0x01267c00, 0x10020c27, // fadd vpm, ra_32_re, r0 +0x019c9e40, 0x10020c27, // fadd vpm, rb_32_im, r1 +0x156a7d80, 0x10021c67, // mov vw_setup, ra_vpm_hi +0x02267c00, 0x10020c27, // fsub vpm, ra_32_re, r0 +0x029c9e40, 0x10020c27, // fsub vpm, rb_32_im, r1 +0x00000000, 0xf0f4c9e7, // bra -, ra_sync +0x009e7000, 0x100009e7, // nop +0x15667d80, 0x10020c67, // mov vr_setup, ra_vpm_lo +0x15c27d80, 0x100009e7, // mov -, vpm +0x00000080, 0xf0f801a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x00000019, 0xe80009e7, // mov -, sacq(i+9) +0x00000001, 0xe80009e7, // mov -, srel(i+1) +0x0000001a, 0xe80009e7, // mov -, sacq(i+9) +0x00000002, 0xe80009e7, // mov -, srel(i+1) +0x0000001b, 0xe80009e7, // mov -, sacq(i+9) +0x00000003, 0xe80009e7, // mov -, srel(i+1) +0x0000001c, 0xe80009e7, // mov -, sacq(i+9) +0x00000004, 0xe80009e7, // mov -, srel(i+1) +0x0000001d, 0xe80009e7, // mov -, sacq(i+9) +0x00000005, 0xe80009e7, // mov -, srel(i+1) +0x0000001e, 0xe80009e7, // mov -, sacq(i+9) +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000006, 0xe80009e7, // mov -, srel(i+1) +0x0000001f, 0xe80009e7, // mov -, sacq(i+9) +0x00000007, 0xe80009e7, // mov -, srel(i+1) +0x00000500, 0xf0f811a7, // brr rx_ptr, label +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x00000009, 0xe80009e7, // mov -, srel(i+9) +0x00000011, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000a, 0xe80009e7, // mov -, srel(i+9) +0x00000012, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000b, 0xe80009e7, // mov -, srel(i+9) +0x00000013, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000c, 0xe80009e7, // mov -, srel(i+9) +0x00000014, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000d, 0xe80009e7, // mov -, srel(i+9) +0x00000015, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000e, 0xe80009e7, // mov -, srel(i+9) +0x00000016, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x00000000, 0xf0f509e7, // bra -, ra_link_1 +0x0000000f, 0xe80009e7, // mov -, srel(i+9) +0x00000017, 0xe80009e7, // mov -, sacq(i+1) +0x009e7000, 0x100009e7, // nop +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> (1<> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffda8, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x959e7009, 0x10024249, // mov ra_32_re, r0; mov rb_32_im, r1 +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0xfffffc90, 0xf0f80027, // brr ra_link_0, call +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x204e7006, 0x100059c2, // nop; fmul ra_temp, r0, ra_tw_re+TW32 +0x209d300f, 0x100049e2, // nop; fmul r2, r1, rb_tw_im+TW32 +0x204e700e, 0x100049e3, // nop; fmul r3, r1, ra_tw_re+TW32 +0x22093c87, 0x10024821, // fsub r0, ra_temp, r2; fmul r1, r0, rb_tw_im+TW32 +0x019e72c0, 0x10020867, // fadd r1, r1, r3 +0x00000000, 0xf0f549e7, // bra -, ra_save_32 +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x95687ff6, 0x10024687, // mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi +0x95701ff6, 0x10024701, // mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffc00, 0xf0f80027, // brr ra_link_0, call +0x009e7000, 0xa00009e7, // nop; ldtmu0 +0x159e7900, 0xa0020827, // mov r0, r4; ldtmu0 +0x159e7900, 0x10020867, // mov r1, r4 +0x00000000, 0xf0f489e7, // bra -, ra_save_16 +0x009e7000, 0x100009e7, // nop +0x95642ff6, 0x10024642, // mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo +0x956c0ff6, 0x100246c0, // mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +0x159c5fc0, 0x10022827, // mov.setf r0, rb_inst +0x0d9c11c0, 0xd0020827, // sub r0, r0, 1 +0x119c51c0, 0xd0020827, // shl r0, r0, 5 +0x0c9c6e00, 0x100601a7, // add.ifnz ra_sync, rx_sync_slave, r0 +0x159c4fc0, 0x10060127, // mov.ifnz ra_save_16, rx_save_slave_16 +0x159cafc0, 0x100602a7, // mov.ifnz ra_save_32, rx_save_slave_32 +0x15827d80, 0x100220e7, // mov.setf ra_addr_x, unif +0x15827d80, 0x100210e7, // mov rb_addr_y, unif +0x00000958, 0xf00809e7, // brr.allz -, r:end +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100204e7, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x100214e7, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c51c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15bdf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x14767180, 0x10020867, // and r1, r0, mask +0x0e9c11c0, 0xd0020827, // shr r0, r0, shift +0x14767180, 0x10020827, // and r0, r0, mask +0x119c13c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147a7180, 0x10020867, // and r1, r0, mask +0x0e9c21c0, 0xd0020827, // shr r0, r0, shift +0x147a7180, 0x10020827, // and r0, r0, mask +0x119c23c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x147e7180, 0x10020867, // and r1, r0, mask +0x0e9c41c0, 0xd0020827, // shr r0, r0, shift +0x147e7180, 0x10020827, // and r0, r0, mask +0x119c43c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x149da1c0, 0x10020867, // and r1, r0, mask +0x0e9c81c0, 0xd0020827, // shr r0, r0, shift +0x149da1c0, 0x10020827, // and r0, r0, mask +0x119c83c0, 0xd0020867, // shl r1, r1, shift +0x159e7040, 0x10020827, // or r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x94981dc0, 0xd00269e2, // and.setf -, elem_num, 1; mov r2, r0 +0x959f1489, 0xd004c820, // mov.ifz r0, r2; mov.ifnz r0, r1 >> 1 +0x959ff252, 0xd0068861, // mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffa98, 0xf0f80227, // brr ra_link_1, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x0e1cddc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffffa70, 0xf00809e7, // brr.allz -, r:pass_1 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dfdc0, 0x100201e7, // add ra_points, ra_points, rb_0x100 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000001, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000002, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffffb20, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0xfffffb00, 0xf0f80227, // brr ra_link_1, r:pass_2 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cddc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff970, 0xf00809e7, // brr.allz -, r:pass_2 +0x00000020, 0xe0020827, // mov r0, 4*8 +0x0d227c00, 0x10020227, // sub ra_link_1, ra_link_1, r0 +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0x950c3dbf, 0x100250c3, // mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000000, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c9cbe00, 0x10020e27, // add t0s, ptr, r0 +0x0c9cbe40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020467, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021467, // mov rb_tw_im+dst, r4 +0x11983dc0, 0xd0020827, // shl r0, elem_num, 3 +0x00000003, 0xe0020867, // mov r1, src +0x119c73c0, 0xd0020867, // shl r1, r1, 7 +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c2e7c00, 0x10020e27, // add t0s, ptr, r0 +0x0c2e7c40, 0x10020e27, // add t0s, ptr, r1 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020327, // mov ra_tw_re+dst, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10021327, // mov rb_tw_im+dst, r4 +0x00000000, 0xe0024492, // mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +0x00000000, 0xe0024514, // mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x159c5fc0, 0x10020827, // mov r0, rb_inst +0x119c41c0, 0xd0020827, // shl r0, r0, m +0x0c9a7180, 0x10020167, // add ra_load_idx, r0, elem_num +0x00000000, 0xe00201e7, // mov ra_points, 0 +0x159c3fc0, 0x10020067, // mov ra_save_ptr, rb_addr_y +0x8c15ddf6, 0x10024160, // add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx +0x119c31c0, 0xd0020827, // shl r0, r0, 3 +0x0c9c41c0, 0xd0020867, // add r1, r0, 4 +0x0c0e7c00, 0x10020e27, // add t0s, ra_addr_x, r0 +0x0c0e7c40, 0x10020e27, // add t0s, ra_addr_x, r1 +0xfffff7b0, 0xf0f80227, // brr ra_link_1, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+base; mov r3, rb_tw_im+base +0x20327016, 0x100049e0, // nop; fmul r0, r2, ra_tw_re+step +0x209cc017, 0x100049e1, // nop; fmul r1, r2, rb_tw_im+step +0x209cc01f, 0x100049e2, // nop; fmul r2, r3, rb_tw_im+step +0x2132709e, 0x100248a3, // fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step +0x029e7640, 0x100208e7, // fsub r3, r3, r1 +0x02467c80, 0x10020827, // fsub r0, a, b +0x02467180, 0x10020867, // fsub r1, r0, a +0x019e7280, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x02467c40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x014a7380, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10025891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x02467c80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100204a7, // fadd a+1, r0, r1 +0x029d1ec0, 0x10020827, // fsub r0, a, b +0x029d11c0, 0x10020867, // fsub r1, r0, a +0x019e72c0, 0x100208a7, // fadd r2, r1, b +0x029e7040, 0x10020867, // fsub r1, r0, r1 +0x029d1e40, 0x10020867, // fsub r1, a, r1 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x019d23c0, 0x10020867, // fadd r1, r1, a+1 +0x019e7040, 0x100208a7, // fadd r2, r0, r1 +0x829e7412, 0x10024891, // fsub r2, r2, r0; mov a, r2 +0x029e7280, 0x10020867, // fsub r1, r1, r2 +0x029d1e80, 0x100208a7, // fsub r2, a, r2 +0x029e7080, 0x10020827, // fsub r0, r0, r2 +0x019e7040, 0x100214a7, // fadd a+1, r0, r1 +0x95451dbf, 0x100248a3, // mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +0x14988dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f8492, 0xd002c462, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f86db, 0xd002d463, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14984dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f4492, 0xd002c422, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f46db, 0xd002d423, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14982dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f2492, 0xd002c3e2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f26db, 0xd002d3e3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x14981dc0, 0xd00229e7, // and.setf -, elem_num, (8>>i) +0x959f1492, 0xd002c3a2, // mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) +0x959f16db, 0xd002d3a3, // mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +0x0e1cddc0, 0xd00229e7, // shr.setf -, ra_points, STAGES +0xfffff620, 0xf00809e7, // brr.allz -, r:pass_3 +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c1dddc0, 0x100201e7, // add ra_points, ra_points, rb_0x80 +0x00000000, 0xf0f4c227, // bra ra_link_1, ra_sync +0x009e7000, 0x100009e7, // nop +0x009e7000, 0xa00009e7, // ldtmu0 +0x009e7000, 0xa00009e7, // ldtmu0 +0xfffff678, 0xf0f809e7, // brr -, r:loop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x159c3fc0, 0x100209a7, // mov interrupt, flag +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_trans.hex b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_trans.hex new file mode 100755 index 0000000..93af75e --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_trans.hex @@ -0,0 +1,126 @@ +0x15827d80, 0x10020e27, // mov t0s, unif +0x009e7000, 0xa00009e7, // ldtmu0 +0x0c9cc9c0, 0xd0020e27, // add t0s, r4, 3*4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x0c827980, 0x100200a7, // add ra_src_base, r4, unif +0x15827d80, 0x10020e27, // mov t0s, unif +0x009e7000, 0xa00009e7, // ldtmu0 +0x0c9cc9c0, 0xd0020e27, // add t0s, r4, 3*4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x0c827980, 0x100200e7, // add ra_dst_base, r4, unif +0x15827d80, 0x100214a7, // mov rb_Y_STRIDE_SRC, unif +0x15827d80, 0x100214e7, // mov rb_Y_STRIDE_DST, unif +0x15827d80, 0x10021527, // mov rb_NX, unif +0x15827d80, 0x10021567, // mov rb_NY, unif +0x00000008, 0xe0021467, // mov rb_X_STRIDE, 2*4 +0x00000010, 0xe0021427, // mov rb_0x10, 0x10 +0xc0000000, 0xe0020827, // mov r0, vdw_setup_1(0) +0x0c9d31c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_DST +0x00000040, 0xe0020867, // mov r1, 16*4 +0x0d9e7040, 0x100201a7, // sub ra_vdw_stride, r0, r1 +0x40991037, 0x100049e0, // nop; mul24 r0, elem_num, rb_X_STRIDE +0x159e7000, 0x10021027, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd0021227, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x10021067, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd0021267, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x100210a7, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd00212a7, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x100210e7, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd00212e7, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x10021127, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd0021327, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x10021167, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd0021367, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x100211a7, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd00213a7, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x159e7000, 0x100211e7, // mov rb_offsets_re+i, r0 +0x0c9c41c0, 0xd00213e7, // add rb_offsets_im+i, r0, 4 +0x0c9d21c0, 0x10020827, // add r0, r0, rb_Y_STRIDE_SRC +0x00000000, 0xe0020067, // mov ra_y, 0 +0x00000000, 0xe0020027, // mov ra_x, 0 +0x40052037, 0x100049e1, // nop; mul24 r1, ra_y, rb_Y_STRIDE_SRC +0x40011037, 0x100049e0, // nop; mul24 r0, ra_x, rb_X_STRIDE +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c0a7c00, 0x10020127, // add ra_src_cell, ra_src_base, r0 +0x40013037, 0x100049e1, // nop; mul24 r1, ra_x, rb_Y_STRIDE_DST +0x40051037, 0x100049e0, // nop; mul24 r0, ra_y, rb_X_STRIDE +0x0c9e7040, 0x10020827, // add r0, r0, r1 +0x0c0e7c00, 0x10020167, // add ra_dst_cell, ra_dst_base, r0 +0x00001200, 0xe0021c67, // mov vw_setup, vpm_setup(16, 1, v32(0,0)) +0x0c100dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re +0x0c108dc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im +0x0c101dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c109dc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c102dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10adc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c103dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10bdc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c104dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10cdc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c105dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10ddc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c106dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10edc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x0c107dc0, 0x10020e27, // add t0s, ra_src_cell, rb_offsets_re+1+i +0x0c10fdc0, 0x10020f27, // add t1s, ra_src_cell, rb_offsets_im+1+i +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xa00009e7, // ldtmu0 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x009e7000, 0xb00009e7, // ldtmu1 +0x159e7900, 0x10020c27, // mov vpm, r4 +0x88104000, 0xe0021c67, // mov vw_setup, vdw_setup_0(16, 16, dma_h32(0,0)) +0x151a7d80, 0x10021c67, // mov vw_setup, ra_vdw_stride +0x15167d80, 0x10021ca7, // mov vw_addr, ra_dst_cell +0x159f2fc0, 0x100009e7, // mov -, vw_wait +0x0c010dc0, 0x10020027, // add ra_x, ra_x, rb_0x10 +0x009e7000, 0x100009e7, // nop +0x0d014dc0, 0x100229e7, // sub.setf -, ra_x, rb_NX +0xfffffde0, 0xf01809e7, // brr.allnz -, r:inner +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x0c048dc0, 0xd0020067, // add ra_y, ra_y, 8 +0x009e7000, 0x100009e7, // nop +0x0d055dc0, 0x100229e7, // sub.setf -, ra_y, rb_NY +0xfffffda0, 0xf01809e7, // brr.allnz -, r:outer +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop +0x00000001, 0xe00209a7, // mov interrupt, 1 +0x009e7000, 0x300009e7, // nop; nop; thrend +0x009e7000, 0x100009e7, // nop +0x009e7000, 0x100009e7, // nop diff --git a/host_applications/linux/apps/hello_pi/hello_fft/mailbox.c b/host_applications/linux/apps/hello_pi/hello_fft/mailbox.c new file mode 100755 index 0000000..de44e81 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/mailbox.c @@ -0,0 +1,262 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mailbox.h" + +#define PAGE_SIZE (4*1024) + +void *mapmem(unsigned base, unsigned size) +{ + int mem_fd; + unsigned offset = base % PAGE_SIZE; + base = base - offset; + size = size + offset; + /* open /dev/mem */ + if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { + printf("can't open /dev/mem\nThis program should be run as root. Try prefixing command with: sudo\n"); + exit (-1); + } + void *mem = mmap( + 0, + size, + PROT_READ|PROT_WRITE, + MAP_SHARED/*|MAP_FIXED*/, + mem_fd, + base); +#ifdef DEBUG + printf("base=0x%x, mem=%p\n", base, mem); +#endif + if (mem == MAP_FAILED) { + printf("mmap error %d\n", (int)mem); + exit (-1); + } + close(mem_fd); + return (char *)mem + offset; +} + +void unmapmem(void *addr, unsigned size) +{ + const intptr_t offset = (intptr_t)addr % PAGE_SIZE; + addr = (char *)addr - offset; + size = size + offset; + int s = munmap(addr, size); + if (s != 0) { + printf("munmap error %d\n", s); + exit (-1); + } +} + +/* + * use ioctl to send mbox property message + */ + +static int mbox_property(int file_desc, void *buf) +{ + int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf); + + if (ret_val < 0) { + printf("ioctl_set_msg failed:%d\n", ret_val); + } + +#ifdef DEBUG + unsigned *p = buf; int i; unsigned size = *(unsigned *)buf; + for (i=0; i + +#define MAJOR_NUM 100 +#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) +#define DEVICE_FILE_NAME "/dev/vcio" + +int mbox_open(); +void mbox_close(int file_desc); + +unsigned get_version(int file_desc); +unsigned mem_alloc(int file_desc, unsigned size, unsigned align, unsigned flags); +unsigned mem_free(int file_desc, unsigned handle); +unsigned mem_lock(int file_desc, unsigned handle); +unsigned mem_unlock(int file_desc, unsigned handle); +void *mapmem(unsigned base, unsigned size); +void unmapmem(void *addr, unsigned size); + +unsigned execute_code(int file_desc, unsigned code, unsigned r0, unsigned r1, unsigned r2, unsigned r3, unsigned r4, unsigned r5); +unsigned execute_qpu(int file_desc, unsigned num_qpus, unsigned control, unsigned noflush, unsigned timeout); +unsigned qpu_enable(int file_desc, unsigned enable); diff --git a/host_applications/linux/apps/hello_pi/hello_fft/makefile b/host_applications/linux/apps/hello_pi/hello_fft/makefile new file mode 100755 index 0000000..c31fece --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/makefile @@ -0,0 +1,36 @@ +S = hex/shader_256.hex \ + hex/shader_512.hex \ + hex/shader_1k.hex \ + hex/shader_2k.hex \ + hex/shader_4k.hex \ + hex/shader_8k.hex \ + hex/shader_16k.hex \ + hex/shader_32k.hex \ + hex/shader_64k.hex \ + hex/shader_128k.hex \ + hex/shader_256k.hex \ + hex/shader_512k.hex \ + hex/shader_1024k.hex \ + hex/shader_2048k.hex \ + hex/shader_4096k.hex + +C = mailbox.c gpu_fft.c gpu_fft_base.c gpu_fft_twiddles.c gpu_fft_shaders.c + +C1D = $(C) hello_fft.c +C2D = $(C) hello_fft_2d.c gpu_fft_trans.c + +H1D = gpu_fft.h mailbox.h +H2D = gpu_fft.h mailbox.h gpu_fft_trans.h hello_fft_2d_bitmap.h + +F = -lrt -lm -ldl + +all: hello_fft.bin hello_fft_2d.bin + +hello_fft.bin: $(S) $(C1D) $(H1D) + gcc -o hello_fft.bin $(F) $(C1D) + +hello_fft_2d.bin: $(S) hex/shader_trans.hex $(C2D) $(H2D) + gcc -o hello_fft_2d.bin $(F) $(C2D) + +clean: + rm -f *.bin diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft.qinc b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft.qinc new file mode 100755 index 0000000..1ff96c4 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft.qinc @@ -0,0 +1,509 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +############################################################################## +# Bit-rotated write + +.set PASS16_STRIDE, ((1<> (1<> (1<> 1 + mov.ifnz r1, r1; mov.ifz r1, r2 << 1 +.endm + +############################################################################## + +.macro read_rev, stride + add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx + + bit_rev 1, rx_0x5555 # 16 SIMD + bit_rev 2, rx_0x3333 + bit_rev 4, rx_0x0F0F + bit_rev 8, rx_0x00FF # reversal creates left shift by 16-STAGES +.if STAGES>13 + shl r0, r0, STAGES-13 +.endif +.if STAGES<13 + shr r0, r0, 13-STAGES +.endif # r0 = re = {idx[0:STAGES-1], 1'b0, 2'b0} + add r1, r0, 4 # r1 = im = {idx[0:STAGES-1], 1'b1, 2'b0} + + interleave + swizzle + + add t0s, ra_addr_x, r0 + add t0s, ra_addr_x, r1 +.endm + +.macro load_rev, stride, call + read_rev stride + nop; ldtmu0 + mov r0, r4; ldtmu0 + mov r1, r4 + brr ra_link_0, call + interleave +.endm + +############################################################################## + +.macro read_lin, stride + add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx + + shl r0, r0, 3 + add r1, r0, 4 + + add t0s, ra_addr_x, r0 + add t0s, ra_addr_x, r1 +.endm + +.macro load_lin, stride, call + read_lin stride + brr ra_link_0, call + nop; ldtmu0 + mov r0, r4; ldtmu0 + mov r1, r4 +.endm + +############################################################################## +# Unpack twiddles + +.macro unpack_twiddles + mov r2, ra_tw_re+TW16+3; mov r3, rb_tw_im+TW16+3 +.rep i, 4 + and.setf -, elem_num, (8>>i) + mov ra_tw_re+TW16+3-i, r2; mov.ifnz r2, r2 >> (8>>i) + mov rb_tw_im+TW16+3-i, r3; mov.ifnz r3, r3 >> (8>>i) +.endr +.endm + +############################################################################## +# float-float enhanced-precision subtract (corrects rounding errors) + +.macro df64_sub32, a, b # df64_sub32(float2 &a, float b) + fsub r0, a, b # float2 s = twoSub(a.x, b); + fsub r1, r0, a + fadd r2, r1, b + fsub r1, r0, r1 + fsub r1, a, r1 + fsub r1, r1, r2 + + fadd r1, r1, a+1 # s.y += a.y; + + fadd r2, r0, r1 # a = twoSum(s.x, s,y); + fsub r2, r2, r0; mov a, r2 + fsub r1, r1, r2 + fsub r2, a, r2 + fsub r0, r0, r2 + fadd a+1, r0, r1 +.endm + +############################################################################## +# Rotate twiddles using enhanced-precision trig recurrence + +.macro rotate, base, step + mov r2, ra_tw_re+base; mov r3, rb_tw_im+base + nop; fmul r0, r2, ra_tw_re+step # a.cos + nop; fmul r1, r2, rb_tw_im+step # b.cos + nop; fmul r2, r3, rb_tw_im+step # b.sin + fadd r2, r0, r2; fmul r3, r3, ra_tw_re+step # a.sin + fsub r3, r3, r1 + df64_sub32 ra_tw_re+base, r2 + df64_sub32 rb_tw_im+base, r3 +.endm + +.macro next_twiddles_32 + rotate TW32, TW32_STEP +.endm + +.macro next_twiddles_16 + rotate TW16+3, TW16_STEP + unpack_twiddles +.endm + +############################################################################## +# Alternate input/output buffers between stages + +.macro swap_buffers + mov rb_addr_y, ra_addr_x; mov ra_addr_x, rb_addr_y +.endm + +############################################################################## +# Reset counters and twiddles + +.macro init_stage, m + mov ra_tw_re+TW16+4, 0; mov rb_tw_im+TW16+4, 0 +.ifset TW32 + mov ra_tw_re+TW32+1, 0; mov rb_tw_im+TW32+1, 0 +.endif + unpack_twiddles + mov r0, rb_inst + shl r0, r0, m + add ra_load_idx, r0, elem_num + mov ra_points, 0 + mov ra_save_ptr, rb_addr_y +.endm + +############################################################################## + +.set LOAD_STRAIGHT, 0 +.set LOAD_REVERSED, 1 + +.macro loader_16, stride, mode + .if mode==LOAD_REVERSED + load_rev stride, r:fft_16 + .else + load_lin stride, r:fft_16 + .endif +.endm + +.macro body_pass_16, mode + loader_16 rb_0x80, mode + bra -, ra_save_16 + nop + mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo + mov ra_vdw_16, rb_vdw_16; mov rb_vdw_16, ra_vdw_16 +.endm + +.macro body_pass_32, mode + loader_16 rb_0xF0, mode + mov ra_32_re, r0; mov rb_32_im, r1 + loader_16 rb_0x10, mode + fft_twiddles_32 + bra -, ra_save_32 + mov ra_vpm_lo, rb_vpm_lo; mov rb_vpm_lo, ra_vpm_lo + mov ra_vpm_hi, rb_vpm_hi; mov rb_vpm_hi, ra_vpm_hi + mov ra_vdw_32, rb_vdw_32; mov rb_vdw_32, ra_vdw_32 +.endm + +.macro body_pass_64, mode, step + loader_16 rb_0x10, mode + mov ra_32_re, r0; mov rb_32_im, r1 + loader_16 rb_0x10, mode + fft_twiddles_32 + + fadd ra_64+0, ra_32_re, r0 + fadd ra_64+1, rb_32_im, r1 + fsub ra_64+2, ra_32_re, r0 + fsub ra_64+3, rb_32_im, r1 + + loader_16 step, mode + mov ra_32_re, r0; mov rb_32_im, r1 + loader_16 rb_0x10, mode + fft_twiddles_32 + + fsub r3, rb_32_im, r1 + fsub r2, ra_32_re, r0 + fadd r1, rb_32_im, r1 + fadd r0, ra_32_re, r0 + + nop; fmul rb_32_im, r1, ra_tw_re+TW48 # ir + nop; fmul ra_32_re, r1, rb_tw_im+TW48 # ii + nop; fmul r1, r0, rb_tw_im+TW48 # ri + fadd rb_64+1, r1, rb_32_im; fmul r0, r0, ra_tw_re+TW48 # rr + fsub rb_64+0, r0, ra_32_re; fmul ra_32_re, r3, rb_tw_im+TW64 # ii + nop; fmul rb_32_im, r3, ra_tw_re+TW64 # ir + bra -, ra_save_64 + nop; fmul r3, r2, rb_tw_im+TW64 # ri + fadd rb_64+3, r3, rb_32_im; fmul r2, r2, ra_tw_re+TW64 # rr + fsub rb_64+2, r2, ra_32_re +.endm + +############################################################################## + +.macro exit, flag + mov interrupt, flag + nop; nop; thrend + nop + nop +.endm + +############################################################################## diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1024k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1024k.qasm new file mode 100755 index 0000000..c3c5205 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1024k.qasm @@ -0,0 +1,319 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 20 + +.include "gpu_fft_ex.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW32_P2_STEP, 2 +.set TW16_P2_STEP, 3 +.set TW32_P3_STEP, 4 +.set TW16_P3_STEP, 5 +.set TW32_P4_STEP, 6 +.set TW16_P4_STEP, 7 + +.set TW32_P4_BASE, 0 # rx_tw_unique +.set TW16_P4_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +# rx_0x00FF00FF rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +# spare ra4 +# spare rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +.set rb_STAGES, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra26 +.set ra_vpm_hi, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x55555555, ra29 +.set rx_0x33333333, ra30 +.set rx_0x0F0F0F0F, ra31 +.set rx_0x00FF00FF, rb0 +.set rx_0x0000FFFF, rb26 + +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0x80, rb29 +.set rb_0xF0, rb30 +.set rb_0x100, rb31 + +############################################################################## +# Constants + +mov rb_STAGES, STAGES + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 + +mov rx_0x55555555, 0x55555555 +mov rx_0x33333333, 0x33333333 +mov rx_0x0F0F0F0F, 0x0F0F0F0F +mov rx_0x00FF00FF, 0x00FF00FF +mov rx_0x0000FFFF, 0x0000FFFF + +mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 +:pass_3 +:pass_4 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x100 + + mov r0, 0x7FFF + and.setf -, ra_points, r0 + + brr.allnz -, r:pass_2 + nop + nop + add.ifnz ra_points, ra_points, rb_0x100 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 4 + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x100 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + mov r0, 3*4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_unique, TW32+0, TW32_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_128k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_128k.qasm new file mode 100755 index 0000000..a1f06ed --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_128k.qasm @@ -0,0 +1,319 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 17 + +.include "gpu_fft_ex.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW16_P2_STEP, 2 +.set TW16_P3_STEP, 3 +.set TW16_P4_STEP, 4 + +.set TW16_P4_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +.set rb_STAGES, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra25 +.set ra_vpm_hi, ra26 +.set ra_vdw_16, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x55555555, ra29 +.set rx_0x33333333, ra30 +.set rx_0x0F0F0F0F, ra31 +.set rx_0x00FF00FF, rb24 +.set rx_0x0000FFFF, rb25 + +.set rb_0x10, rb26 +.set rb_0x40, rb27 +.set rb_0x80, rb28 +.set rb_0xF0, rb29 +.set rb_0x100, rb30 +.set rb_0xFFF, rb31 + +############################################################################## +# Constants + +mov rb_STAGES, STAGES + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 +mov rb_0xFFF, 0xFFF + +mov rx_0x55555555, 0x55555555 +mov rx_0x33333333, 0x33333333 +mov rx_0x0F0F0F0F, 0x0F0F0F0F +mov rx_0x00FF00FF, 0x00FF00FF +mov rx_0x0000FFFF, 0x0000FFFF + +mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 +:pass_3 +:pass_4 + body_pass_16 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + and.setf -, ra_points, rb_0xFFF + + brr.allnz -, r:pass_2 + nop + nop + add.ifnz ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + init_stage 4 + read_lin rb_0x80 + + .rep i, 2 + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + .endr + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + mov r0, 4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_16k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_16k.qasm new file mode 100755 index 0000000..177890b --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_16k.qasm @@ -0,0 +1,282 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 14 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW32_P2_STEP, 2 +.set TW16_P2_STEP, 3 +.set TW16_P3_STEP, 4 + +.set TW16_P3_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra25 +.set ra_vpm_hi, ra26 +.set ra_vdw_16, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x5555, ra29 +.set rx_0x3333, ra30 +.set rx_0x0F0F, ra31 + +.set rx_0x00FF, rb26 +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0x80, rb29 +.set rb_0xF0, rb30 +.set rb_0x100, rb31 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 + body_pass_32 LOAD_STRAIGHT + +:pass_3 + body_pass_16 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 2 + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x100 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + mov r0, 4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P3_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1k.qasm new file mode 100755 index 0000000..31314ae --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1k.qasm @@ -0,0 +1,231 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 10 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_P1_BASE, 0 # rx_tw_shared +.set TW16_P1_BASE, 1 +.set TW32_P2_STEP, 2 +.set TW16_P2_STEP, 3 + +.set TW32_P2_BASE, 0 # rx_tw_unique +.set TW16_P2_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +# rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 + +.set rx_tw_shared, ra10 +.set rx_tw_unique, rb10 + +.set ra_tw_re, ra11 # 9 +.set rb_tw_im, rb11 # 9 + +.set ra_vpm_lo, ra27 +.set ra_vpm_hi, ra28 +.set ra_vdw_32, ra29 + +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0xF0, rb29 + +.set rx_0x5555, ra30 +.set rx_0x3333, rb30 +.set rx_0x0F0F, ra31 +.set rx_0x00FF, rb31 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0xF0, 0xF0 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_P1_BASE + load_tw rx_tw_shared, TW32+0, TW32_P1_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P2_BASE + load_tw rx_tw_unique, TW32+0, TW32_P2_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qasm new file mode 100755 index 0000000..bc904f2 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qasm @@ -0,0 +1,336 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 21 + +.include "gpu_fft_2048k.qinc" + +############################################################################## +# Twiddles: src + +.set TW64_BASE0, 0 # rx_tw_shared +.set TW64_BASE1, 1 +.set TW32_BASE, 2 +.set TW16_BASE, 3 +.set TW32_P2_STEP, 4 +.set TW16_P2_STEP, 5 +.set TW32_P3_STEP, 6 +.set TW16_P3_STEP, 7 +.set TW32_P4_STEP, 8 +.set TW16_P4_STEP, 9 + +.set TW32_P4_BASE, 0 # rx_tw_unique +.set TW16_P4_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 +.set TW48, 9 # 1 +.set TW64, 10 # 1 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vpm, rb0 +.set ra_save_ptr, ra1 +.set rb_vpm_16, rb1 +.set ra_temp, ra2 +.set rb_vpm_32, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_48, rb7 +.set ra_link_1, ra8 +.set rb_0x10, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_64, ra10 +.set rx_save_slave_64, rb10 + +.set ra_64, ra11 # 4 +.set rb_64, rb11 # 4 + +.set rx_tw_shared, ra15 +.set rx_tw_unique, rb15 + +.set ra_tw_re, ra16 # 11 +.set rb_tw_im, rb16 # 11 + +############################################################################## +# Dual-use registers + +.set rb_STAGES, rb_64+0 +.set rb_0xF0, rb_64+1 +.set rb_0x40, rb_64+2 + +.set ra_vpm_lo, ra_64+0 +.set ra_vpm_hi, ra_64+1 +.set rb_vpm_lo, rb_vpm_32 +.set rb_vpm_hi, rb_vpm_48 +.set ra_vdw_32, ra_64+3 +.set rb_vdw_32, rb_64+3 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov r5rep, 0x1D0 + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_save_64, r:1f +body_ra_save_64 +:1 + +proc rx_save_slave_64, r:1f +body_rx_save_slave_64 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_64 LOAD_REVERSED, r5 + +:pass_2 +:pass_3 +:pass_4 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + mov.ifnz ra_save_64, rx_save_slave_64 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW48, TW64_BASE0 + load_tw rx_tw_shared, TW64, TW64_BASE1 + init_stage 6 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + mov r1, STAGES + shr.setf -, ra_points, r1 + + brr.allz -, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Dual-use registers + + mov ra_vpm_lo, rb_vpm + mov ra_vpm_hi, rb_vpm_16 + + mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) + mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) + + mov rb_STAGES, STAGES + mov rb_0xF0, 0xF0 + mov rb_0x40, 0x40 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + mov r0, 0x7FFF + and.setf -, ra_points, r0 + + brr.allnz -, r:pass_2 + nop + mov r0, 0x100 + add.ifnz ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 4 + brr ra_link_1, r:pass_3 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + mov r0, 0x100 + brr.allz -, r:pass_3 + add ra_points, ra_points, r0 + mov r0, (4-1)*4*8 + sub ra_link_1, ra_link_1, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_unique, TW32+0, TW32_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_4 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qinc b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qinc new file mode 100755 index 0000000..680f7c4 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qinc @@ -0,0 +1,91 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +############################################################################## +# Macro baseline + +.include "gpu_fft_ex.qinc" + +############################################################################## +# Redefining some macros + +.macro body_ra_save_64 + mov -, vw_wait + + .rep i, 7 + mov -, srel(i+1) # Master releases slaves + .endr + + write_vpm_64 + + .rep i, 7 + mov -, sacq(i+9) # Master waits for slaves + .endr + + mov r0, vdw_setup_0(1, 16, dma_h32(0,0)) + mov r1, 0x40 + add ra_save_ptr, ra_save_ptr, r1; mov r1, ra_save_ptr + mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) + mov r3, PASS64_STRIDE + + .rep i, 64 + add r0, r0, r2; mov vw_setup, r0 + add r1, r1, r3; mov vw_addr, r1 + .endr + + bra -, ra_link_1 + nop + nop + nop +.endm + +.macro bit_rev, shift, mask + mov r2, mask + and r1, r0, r2 + shr r0, r0, shift + and r0, r0, r2 + shl r1, r1, shift + or r0, r0, r1 +.endm + +.macro read_rev, stride + add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx + + bit_rev 1, 0x55555555 # 16 SIMD + bit_rev 2, 0x33333333 + bit_rev 4, 0x0F0F0F0F + bit_rev 8, 0x00FF00FF + bit_rev rb_0x10, 0x0000FFFF + + shr r0, r0, 32-STAGES-3 # r0 = re = {idx[0:STAGES-1], 1'b0, 2'b0} + add r1, r0, 4 # r1 = im = {idx[0:STAGES-1], 1'b1, 2'b0} + + interleave + + add t0s, ra_addr_x, r0 + add t0s, ra_addr_x, r1 +.endm diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256.qasm new file mode 100755 index 0000000..c6328aa --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256.qasm @@ -0,0 +1,233 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 8 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW16_P1_BASE, 0 # rx_tw_shared +.set TW16_P2_STEP, 1 + +.set TW16_P2_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW16, 1 # 5 + +############################################################################## +# Registers + +.set ra_link_1, ra0 +# rb0 +.set ra_save_ptr, ra1 +# rb1 +.set ra_temp, ra2 +# rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +# rb7 + +.set rx_tw_shared, ra8 +.set rx_tw_unique, rb8 + +.set ra_tw_re, ra9 # 6 +.set rb_tw_im, rb9 # 6 + +.set ra_vpm, ra27 +.set rb_vpm, rb27 +.set ra_vdw, ra28 +.set rb_vdw, rb28 + +.set rx_0x5555, ra29 +.set rx_0x3333, ra30 +.set rx_0x0F0F, ra31 + +.set rb_0x40, rb30 +.set rb_0x80, rb31 + +############################################################################## +# Register alias + +.set ra_link_0, ra_save_16 + +############################################################################## +# Constants + +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F + +mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm, rb_vpm, -, - + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm, ra_vdw +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Redefining this macro + +.macro read_rev, stride + add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx + + bit_rev 1, rx_0x5555 # 16 SIMD + bit_rev 2, rx_0x3333 + bit_rev 4, rx_0x0F0F + + shl r0, r0, 3 # {idx[0:7], 1'b0, 2'b0} + add r1, r0, 4 # {idx[0:7], 1'b1, 2'b0} + + add t0s, ra_addr_x, r0 + add t0s, ra_addr_x, r1 +.endm + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 +:pass_2 + brr -, r:fft_16 + nop; ldtmu0 + mov r0, r4; ldtmu0 + mov r1, r4 + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_P1_BASE + init_stage 4 + read_rev rb_0x80 + read_rev rb_0x80 + +.rep i, 2 + brr ra_link_1, r:pass_1 + nop + mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm + mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +.endr + + bra ra_link_1, ra_sync + nop + nop + nop + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P2_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + read_lin rb_0x80 + + brr ra_link_1, r:pass_2 + nop + mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm + mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw + + next_twiddles_16 + + brr ra_link_1, r:pass_2 + nop + mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm + mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw + + bra ra_link_1, ra_sync + nop + nop + nop + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256k.qasm new file mode 100755 index 0000000..759eb69 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256k.qasm @@ -0,0 +1,326 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 18 + +.include "gpu_fft_ex.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW16_P2_STEP, 2 +.set TW16_P3_STEP, 3 +.set TW32_P4_STEP, 4 +.set TW16_P4_STEP, 5 + +.set TW32_P4_BASE, 0 # rx_tw_unique +.set TW16_P4_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +.set rb_STAGES, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra25 +.set ra_vpm_hi, ra26 +.set ra_vdw_16, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x55555555, ra29 +.set rx_0x33333333, ra30 +.set rx_0x0F0F0F0F, ra31 +.set rx_0x00FF00FF, rb24 +.set rx_0x0000FFFF, rb25 + +.set rb_0x10, rb26 +.set rb_0x40, rb27 +.set rb_0x80, rb28 +.set rb_0xF0, rb29 +.set rb_0x100, rb30 +.set rb_0x1FFF, rb31 + +############################################################################## +# Constants + +mov rb_STAGES, STAGES + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 +mov rb_0x1FFF, 0x1FFF + +mov rx_0x55555555, 0x55555555 +mov rx_0x33333333, 0x33333333 +mov rx_0x0F0F0F0F, 0x0F0F0F0F +mov rx_0x00FF00FF, 0x00FF00FF +mov rx_0x0000FFFF, 0x0000FFFF + +mov ra_vdw_16, vdw_setup_0( 1, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0( 1, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 +:pass_3 + body_pass_16 LOAD_STRAIGHT + +:pass_4 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + and.setf -, ra_points, rb_0x1FFF + + brr.allnz -, r:pass_2 + nop + nop + add.ifnz ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + init_stage 4 + read_lin rb_0x80 + + .rep i, 4 + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + .endr + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + mov r0, 3*4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_unique, TW32+0, TW32_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2k.qasm new file mode 100755 index 0000000..304b144 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2k.qasm @@ -0,0 +1,265 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 11 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW64_P1_BASE0, 0 # rx_tw_shared +.set TW64_P1_BASE1, 1 +.set TW32_P1_BASE, 2 +.set TW16_P1_BASE, 3 +.set TW32_P2_STEP, 4 +.set TW16_P2_STEP, 5 + +.set TW32_P2_BASE, 0 # rx_tw_unique +.set TW16_P2_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 +.set TW48, 9 # 1 +.set TW64, 10 # 1 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vpm, rb0 +.set ra_save_ptr, ra1 +.set rb_vpm_16, rb1 +.set ra_temp, ra2 +.set rb_vpm_32, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_48, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_64, ra10 +.set rx_save_slave_64, rb10 + +.set ra_64, ra11 # 4 +.set rb_64, rb11 # 4 + +.set rx_tw_shared, ra15 +.set rx_tw_unique, rb15 + +.set ra_tw_re, ra16 # 11 +.set rb_tw_im, rb16 # 11 + +.set rx_0x5555, ra28 +.set rx_0x3333, ra29 +.set rx_0x0F0F, ra30 +.set rx_0x00FF, ra31 + +.set rb_0x10, rb28 +.set rb_0x40, rb29 +.set rb_0xF0, rb30 +.set rb_0x1D0, rb31 + +############################################################################## +# Dual-use registers + +.set ra_vpm_lo, ra_64+0 +.set ra_vpm_hi, ra_64+1 +.set rb_vpm_lo, rb_vpm_32 +.set rb_vpm_hi, rb_vpm_48 +.set ra_vdw_32, ra_64+2 +.set rb_vdw_32, rb_64+2 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0xF0, 0xF0 +mov rb_0x1D0, 0x1D0 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_save_64, r:1f +body_ra_save_64 rb_0x40 +:1 + +proc rx_save_slave_64, r:1f +body_rx_save_slave_64 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_64 LOAD_REVERSED, rb_0x1D0 + +:pass_2 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + mov.ifnz ra_save_64, rx_save_slave_64 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_P1_BASE + load_tw rx_tw_shared, TW32+0, TW32_P1_BASE + load_tw rx_tw_shared, TW48, TW64_P1_BASE0 + load_tw rx_tw_shared, TW64, TW64_P1_BASE1 + init_stage 6 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Dual-use registers + + mov ra_vpm_lo, rb_vpm + mov ra_vpm_hi, rb_vpm_16 + + mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) + mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P2_BASE + load_tw rx_tw_unique, TW32+0, TW32_P2_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_32k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_32k.qasm new file mode 100755 index 0000000..d0baf9c --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_32k.qasm @@ -0,0 +1,271 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 15 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW32_P2_STEP, 2 +.set TW16_P2_STEP, 3 +.set TW32_P3_STEP, 4 +.set TW16_P3_STEP, 5 + +.set TW32_P3_BASE, 0 # rx_tw_unique +.set TW16_P3_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +# rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 + +.set rx_tw_shared, ra10 +.set rx_tw_unique, rb10 + +.set ra_tw_re, ra11 # 9 +.set rb_tw_im, rb11 # 9 + +.set ra_vpm_lo, ra26 +.set ra_vpm_hi, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x5555, ra29 +.set rx_0x3333, ra30 +.set rx_0x0F0F, ra31 + +.set rx_0x00FF, rb26 +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0x80, rb29 +.set rb_0xF0, rb30 +.set rb_0x100, rb31 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 +:pass_3 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 4 + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x100 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + mov r0, 3*4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P3_BASE + load_tw rx_tw_unique, TW32+0, TW32_P3_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x100 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4096k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4096k.qasm new file mode 100755 index 0000000..831f5d0 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4096k.qasm @@ -0,0 +1,356 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 22 + +.include "gpu_fft_2048k.qinc" + +############################################################################## +# Twiddles: src + +.set TW64_BASE0, 0 # rx_tw_shared +.set TW64_BASE1, 1 +.set TW32_BASE, 2 +.set TW16_BASE, 3 + +.set TW48_P2_STEP, 4 +.set TW64_P2_STEP, 5 + +.set TW32_P2_STEP, 6 +.set TW16_P2_STEP, 7 +.set TW32_P3_STEP, 8 +.set TW16_P3_STEP, 9 +.set TW32_P4_STEP, 10 +.set TW16_P4_STEP, 11 + +.set TW32_P4_BASE, 0 # rx_tw_unique +.set TW16_P4_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 +.set TW48, 9 # 2 +.set TW64, 11 # 2 +.set TW48_STEP, 13 # 1 +.set TW64_STEP, 14 # 1 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vpm, rb0 +.set ra_save_ptr, ra1 +.set rb_vpm_16, rb1 +.set ra_temp, ra2 +.set rb_vpm_32, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_48, rb7 +.set ra_link_1, ra8 +.set rb_0x10, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_64, ra10 +.set rx_save_slave_64, rb10 + +.set ra_64, ra11 # 4 +.set rb_64, rb11 # 4 + +.set rx_tw_shared, ra15 +.set rx_tw_unique, rb15 + +.set ra_tw_re, ra16 # 15 +.set rb_tw_im, rb16 # 15 + +############################################################################## +# Dual-use registers + +.set rb_STAGES, rb_64+0 +.set rb_0xF0, rb_64+1 +.set rb_0x40, rb_64+2 + +.set ra_vpm_lo, ra_64+0 +.set ra_vpm_hi, ra_64+1 +.set rb_vpm_lo, rb_vpm_32 +.set rb_vpm_hi, rb_vpm_48 +.set ra_vdw_32, ra_64+3 +.set rb_vdw_32, rb_64+3 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov r5rep, 0x1D0 + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_save_64, r:1f +body_ra_save_64 +:1 + +proc rx_save_slave_64, r:1f +body_rx_save_slave_64 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_64 LOAD_REVERSED, r5 +:pass_2 + body_pass_64 LOAD_STRAIGHT, r5 +:pass_3 +:pass_4 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + mov.ifnz ra_save_64, rx_save_slave_64 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW48+0, TW64_BASE0 + load_tw rx_tw_shared, TW64+0, TW64_BASE1 + init_stage 6 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + mov r1, STAGES + shr.setf -, ra_points, r1 + + brr.allz -, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW48+0, TW64_BASE0 + load_tw rx_tw_shared, TW64+0, TW64_BASE1 + mov ra_tw_re+TW48+1, 0; mov rb_tw_im+TW48+1, 0 + mov ra_tw_re+TW64+1, 0; mov rb_tw_im+TW64+1, 0 + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + load_tw rx_tw_shared, TW48_STEP, TW48_P2_STEP + load_tw rx_tw_shared, TW64_STEP, TW64_P2_STEP + init_stage 6 + read_lin rb_0x10 + + brr ra_link_1, r:pass_2 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + mov r0, 0xFFFF + and.setf -, ra_points, r0 + + brr.allnz -, r:pass_2 + nop + mov r0, 0x200 + add.ifnz ra_points, ra_points, r0 + + rotate TW64, TW64_STEP + rotate TW48, TW48_STEP + next_twiddles_32 + next_twiddles_16 + + mov r1, STAGES + shr.setf -, ra_points, r1 + + brr.allz -, r:pass_2 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Dual-use registers + + mov ra_vpm_lo, rb_vpm + mov ra_vpm_hi, rb_vpm_16 + + mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) + mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) + + mov rb_STAGES, STAGES + mov rb_0xF0, 0xF0 + mov rb_0x40, 0x40 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_3 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + mov r0, 0x3FF + and.setf -, ra_points, r0 + + brr.allnz -, r:pass_3 + nop + mov r0, 0x100 + add.ifnz ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_unique, TW32+0, TW32_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_4 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4k.qasm new file mode 100755 index 0000000..0b2b558 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4k.qasm @@ -0,0 +1,276 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 12 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW16_BASE, 0 # rx_tw_shared +.set TW16_P2_STEP, 1 +.set TW16_P3_STEP, 2 + +.set TW16_P3_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW16, 1 # 5 + +############################################################################## +# Registers + +.set ra_link_1, ra0 +# rb0 +.set ra_save_ptr, ra1 +# rb1 +.set ra_temp, ra2 +# rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +# rb7 + +.set rx_tw_shared, ra8 +.set rx_tw_unique, rb8 + +.set ra_tw_re, ra9 # 6 +.set rb_tw_im, rb9 # 6 + +.set ra_vpm, ra26 +.set rb_vpm, rb26 +.set ra_vdw, ra27 +.set rb_vdw, rb27 + +.set rx_0x5555, ra28 +.set rx_0x3333, ra29 +.set rx_0x0F0F, ra30 +.set rx_0x00FF, ra31 + +.set rb_0x20, rb29 +.set rb_0x40, rb30 +.set rb_0x80, rb31 + +############################################################################## +# Register alias + +.set ra_link_0, ra_save_16 + +############################################################################## +# Constants + +mov rb_0x20, 0x20 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw, vdw_setup_0(16, 16, dma_h32(16,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm, rb_vpm, -, - + +############################################################################## +# Macros + +.macro swap_vpm_vdw + mov ra_vpm, rb_vpm; mov rb_vpm, ra_vpm + mov ra_vdw, rb_vdw; mov rb_vdw, ra_vdw +.endm + +.macro swizzle + mov.setf -, [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] + mov r2, r0; mov.ifnz r0, r0 << 6 + mov r3, r1; mov.ifnz r1, r1 << 6 + mov.setf -, [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0] + nop; mov.ifnz r0, r2 >> 6 + nop; mov.ifnz r1, r3 >> 6 +.endm + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm, ra_vdw +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + read_rev rb_0x80 + nop; ldtmu0 + mov r0, r4; ldtmu0 + mov r1, r4 + swizzle + brr -, r:fft_16 + interleave + +:pass_2 +:pass_3 + read_lin rb_0x80 + brr -, r:fft_16 + nop; ldtmu0 + mov r0, r4; ldtmu0 + mov r1, r4 + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + init_stage 4 + read_rev rb_0x80 + + brr ra_link_1, r:pass_1 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + .rep i, 2 + brr ra_link_1, r:pass_2 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + .endr + + sub ra_link_1, ra_link_1, rb_0x20 + next_twiddles_16 + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P3_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_3 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + + next_twiddles_16 + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_3 + swap_vpm_vdw + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512.qasm new file mode 100755 index 0000000..13e4071 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512.qasm @@ -0,0 +1,240 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 9 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_P1_BASE, 0 # rx_tw_shared +.set TW16_P1_BASE, 1 +.set TW16_P2_STEP, 2 + +.set TW16_P2_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra24 +.set ra_vpm_hi, ra25 +.set ra_vdw_16, ra26 +.set ra_vdw_32, ra27 + +.set rx_0x5555, ra28 +.set rx_0x3333, ra29 +.set rx_0x0F0F, ra30 +.set rx_0x00FF, ra31 + +.set rb_0x10, rb28 +.set rb_0x40, rb29 +.set rb_0x80, rb30 +.set rb_0xF0, rb31 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 + body_pass_16 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_P1_BASE + load_tw rx_tw_shared, TW32+0, TW32_P1_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + nop + + brr ra_link_1, r:pass_1 + nop + nop + nop + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P2_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512k.qasm new file mode 100755 index 0000000..3722639 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512k.qasm @@ -0,0 +1,329 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 19 + +.include "gpu_fft_ex.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW16_P2_STEP, 2 +.set TW32_P3_STEP, 3 +.set TW16_P3_STEP, 4 +.set TW32_P4_STEP, 5 +.set TW16_P4_STEP, 6 + +.set TW32_P4_BASE, 0 # rx_tw_unique +.set TW16_P4_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +.set rb_STAGES, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra25 +.set ra_vpm_hi, ra26 +.set ra_vdw_16, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x55555555, ra29 +.set rx_0x33333333, ra30 +.set rx_0x0F0F0F0F, ra31 +.set rx_0x00FF00FF, rb25 +.set rx_0x0000FFFF, rb26 + +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0x80, rb29 +.set rb_0xF0, rb30 +.set rb_0x100, rb31 + +############################################################################## +# Constants + +mov rb_STAGES, STAGES + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 + +mov rx_0x55555555, 0x55555555 +mov rx_0x33333333, 0x33333333 +mov rx_0x0F0F0F0F, 0x0F0F0F0F +mov rx_0x00FF00FF, 0x00FF00FF +mov rx_0x0000FFFF, 0x0000FFFF + +mov ra_vdw_16, vdw_setup_0(1, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0(1, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(1, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(1, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 + body_pass_16 LOAD_STRAIGHT + +:pass_3 +:pass_4 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + mov r0, 0x3FFF + and.setf -, ra_points, r0 + + brr.allnz -, r:pass_2 + nop + nop + add.ifnz ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 4 + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x100 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + mov r0, 3*4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 4 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P4_BASE + load_tw rx_tw_unique, TW32+0, TW32_P4_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P4_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P4_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_4 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_64k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_64k.qasm new file mode 100755 index 0000000..1a94548 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_64k.qasm @@ -0,0 +1,306 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 16 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW64_BASE0, 0 # rx_tw_shared +.set TW64_BASE1, 1 +.set TW32_BASE, 2 +.set TW16_BASE, 3 +.set TW32_P2_STEP, 4 +.set TW16_P2_STEP, 5 +.set TW32_P3_STEP, 6 +.set TW16_P3_STEP, 7 + +.set TW32_P3_BASE, 0 # rx_tw_unique +.set TW16_P3_BASE, 1 + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 +.set TW48, 9 # 1 +.set TW64, 10 # 1 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vpm, rb0 +.set ra_save_ptr, ra1 +.set rb_vpm_16, rb1 +.set ra_temp, ra2 +.set rb_vpm_32, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_32, ra4 +.set rx_save_slave_32, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_48, rb7 +.set ra_link_1, ra8 +.set rb_0x10, rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_64, ra10 +.set rx_save_slave_64, rb10 + +.set ra_64, ra11 # 4 +.set rb_64, rb11 # 4 + +.set rx_tw_shared, ra15 +.set rx_tw_unique, rb15 + +.set ra_tw_re, ra16 # 11 +.set rb_tw_im, rb16 # 11 + +.set rx_0x5555, ra30 +.set rx_0x3333, rb30 +.set rx_0x0F0F, ra31 +.set rx_0x00FF, rb31 + +############################################################################## +# Dual-use registers + +.set rb_STAGES, rb_0x10 + +.set rb_3x4x8, rb_64+0 +.set rb_0xF0, rb_64+1 +.set rb_0x40, rb_64+2 + +.set ra_vpm_lo, ra_64+0 +.set ra_vpm_hi, ra_64+1 +.set rb_vpm_lo, rb_vpm_32 +.set rb_vpm_hi, rb_vpm_48 +.set ra_vdw_32, ra_64+3 +.set rb_vdw_32, rb_64+3 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov r5rep, 0x1D0 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, rb_vpm, rb_vpm_16, rb_vpm_32, rb_vpm_48 + +############################################################################## +# Master/slave procedures + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_save_64, r:1f + mov r0, 0x40 +body_ra_save_64 r0 +:1 + +proc rx_save_slave_64, r:1f +body_rx_save_slave_64 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_64 LOAD_REVERSED, r5 + +:pass_2 +:pass_3 + body_pass_32 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_32, rx_save_slave_32 + mov.ifnz ra_save_64, rx_save_slave_64 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW48, TW64_BASE0 + load_tw rx_tw_shared, TW64, TW64_BASE1 + init_stage 6 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + mov r1, STAGES + shr.setf -, ra_points, r1 + + brr.allz -, r:pass_1 + nop + mov r0, 0x200 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Dual-use registers + + mov ra_vpm_lo, rb_vpm + mov ra_vpm_hi, rb_vpm_16 + + mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) + mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + + mov rb_3x4x8, 3*4*8 + mov rb_0xF0, 0xF0 + mov rb_0x40, 0x40 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P2_STEP + init_stage 5 + read_lin rb_0x10 + + .rep i, 4 + brr ra_link_1, r:pass_2 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + .endr + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_2 + mov r0, 0x100 + add ra_points, ra_points, r0 + sub ra_link_1, ra_link_1, rb_3x4x8 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P3_BASE + load_tw rx_tw_unique, TW32+0, TW32_P3_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + load_tw rx_tw_shared, TW32_STEP, TW32_P3_STEP + init_stage 5 + read_lin rb_0x10 + + brr ra_link_1, r:pass_3 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + next_twiddles_32 + next_twiddles_16 + + shr.setf -, ra_points, rb_STAGES + + brr.allz -, r:pass_3 + nop + mov r0, 0x100 + add ra_points, ra_points, r0 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_8k.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_8k.qasm new file mode 100755 index 0000000..31acb77 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_8k.qasm @@ -0,0 +1,276 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set STAGES, 13 + +.include "gpu_fft.qinc" + +############################################################################## +# Twiddles: src + +.set TW32_BASE, 0 # rx_tw_shared +.set TW16_BASE, 1 +.set TW16_P2_STEP, 2 +.set TW16_P3_STEP, 3 + +.set TW16_P3_BASE, 0 # rx_tw_unique + +############################################################################## +# Twiddles: dst + +.set TW16_STEP, 0 # 1 +.set TW32_STEP, 1 # 1 +.set TW16, 2 # 5 +.set TW32, 7 # 2 + +############################################################################## +# Registers + +.set ra_link_0, ra0 +.set rb_vdw_16, rb0 +.set ra_save_ptr, ra1 +.set rb_vdw_32, rb1 +.set ra_temp, ra2 +.set rb_vpm_lo, rb2 +.set ra_addr_x, ra3 +.set rb_addr_y, rb3 +.set ra_save_16, ra4 +.set rx_save_slave_16, rb4 +.set ra_load_idx, ra5 +.set rb_inst, rb5 +.set ra_sync, ra6 +.set rx_sync_slave, rb6 +.set ra_points, ra7 +.set rb_vpm_hi, rb7 +.set ra_link_1, ra8 +# rb8 +.set ra_32_re, ra9 +.set rb_32_im, rb9 +.set ra_save_32, ra10 +.set rx_save_slave_32, rb10 + +.set rx_tw_shared, ra11 +.set rx_tw_unique, rb11 + +.set ra_tw_re, ra12 # 9 +.set rb_tw_im, rb12 # 9 + +.set ra_vpm_lo, ra25 +.set ra_vpm_hi, ra26 +.set ra_vdw_16, ra27 +.set ra_vdw_32, ra28 + +.set rx_0x5555, ra29 +.set rx_0x3333, ra30 +.set rx_0x0F0F, ra31 + +.set rx_0x00FF, rb26 +.set rb_0x10, rb27 +.set rb_0x40, rb28 +.set rb_0x80, rb29 +.set rb_0xF0, rb30 +.set rb_0x100, rb31 + +############################################################################## +# Constants + +mov rb_0x10, 0x10 +mov rb_0x40, 0x40 +mov rb_0x80, 0x80 +mov rb_0xF0, 0xF0 +mov rb_0x100, 0x100 + +mov rx_0x5555, 0x5555 +mov rx_0x3333, 0x3333 +mov rx_0x0F0F, 0x0F0F +mov rx_0x00FF, 0x00FF + +mov ra_vdw_16, vdw_setup_0(16, 16, dma_h32( 0,0)) +mov rb_vdw_16, vdw_setup_0(16, 16, dma_h32(32,0)) +mov ra_vdw_32, vdw_setup_0(32, 16, dma_h32( 0,0)) +mov rb_vdw_32, vdw_setup_0(32, 16, dma_h32(32,0)) + +############################################################################## +# Twiddles: ptr + +mov rx_tw_shared, unif +mov rx_tw_unique, unif + +############################################################################## +# Instance + +mov rb_inst, unif +inst_vpm rb_inst, ra_vpm_lo, ra_vpm_hi, rb_vpm_lo, rb_vpm_hi + +############################################################################## +# Master/slave procedures + +proc ra_save_16, r:1f +body_ra_save_16 ra_vpm_lo, ra_vdw_16 +:1 + +proc rx_save_slave_16, r:1f +body_rx_save_slave_16 ra_vpm_lo +:1 + +proc ra_save_32, r:1f +body_ra_save_32 +:1 + +proc rx_save_slave_32, r:1f +body_rx_save_slave_32 +:1 + +proc ra_sync, r:1f +body_ra_sync +:1 + +proc rx_sync_slave, r:main +body_rx_sync_slave + +############################################################################## +# Subroutines + +:fft_16 + body_fft_16 + +:pass_1 + body_pass_32 LOAD_REVERSED + +:pass_2 +:pass_3 + body_pass_16 LOAD_STRAIGHT + +############################################################################## +# Top level + +:main + mov.setf r0, rb_inst + sub r0, r0, 1 + shl r0, r0, 5 + add.ifnz ra_sync, rx_sync_slave, r0 + mov.ifnz ra_save_16, rx_save_slave_16 + mov.ifnz ra_save_32, rx_save_slave_32 + +:loop + mov.setf ra_addr_x, unif # Ping buffer or null + mov rb_addr_y, unif # Pong buffer or IRQ enable + + brr.allz -, r:end + +############################################################################## +# Pass 1 + + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW32+0, TW32_BASE + init_stage 5 + read_rev rb_0x10 + + brr ra_link_1, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_1 + nop + nop + add ra_points, ra_points, rb_0x100 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 2 + + swap_buffers + load_tw rx_tw_shared, TW16+3, TW16_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P2_STEP + init_stage 4 + read_lin rb_0x80 + + .rep i, 2 + brr ra_link_1, r:pass_2 + nop + nop + add ra_points, ra_points, rb_0x80 + .endr + + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_2 + mov r0, 4*8 + sub ra_link_1, ra_link_1, r0 + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## +# Pass 3 + + swap_buffers + load_tw rx_tw_unique, TW16+3, TW16_P3_BASE + load_tw rx_tw_shared, TW16_STEP, TW16_P3_STEP + init_stage 4 + read_lin rb_0x80 + + brr ra_link_1, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + + next_twiddles_16 + + shr.setf -, ra_points, STAGES + + brr.allz -, r:pass_3 + nop + nop + add ra_points, ra_points, rb_0x80 + + bra ra_link_1, ra_sync + nop + ldtmu0 + ldtmu0 + +############################################################################## + + brr -, r:loop + nop + nop + nop + +:end + exit rb_addr_y diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_ex.qinc b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_ex.qinc new file mode 100755 index 0000000..12acdb1 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_ex.qinc @@ -0,0 +1,112 @@ +# BCM2835 "GPU_FFT" release 3.0 +# +# Copyright (c) 2015, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +############################################################################## +# Macro baseline + +.include "gpu_fft.qinc" + +############################################################################## +# Redefining some macros + +.if STAGES>16 +.macro read_rev, stride + add ra_load_idx, ra_load_idx, stride; mov r0, ra_load_idx + + bit_rev 1, rx_0x55555555 # 16 SIMD + bit_rev 2, rx_0x33333333 + bit_rev 4, rx_0x0F0F0F0F + bit_rev 8, rx_0x00FF00FF + bit_rev rb_0x10, rx_0x0000FFFF + + shr r0, r0, 32-STAGES-3 # r0 = re = {idx[0:STAGES-1], 1'b0, 2'b0} + add r1, r0, 4 # r1 = im = {idx[0:STAGES-1], 1'b1, 2'b0} + + interleave + + add t0s, ra_addr_x, r0 + add t0s, ra_addr_x, r1 +.endm +.endif + +.if STAGES>17 +.macro body_ra_save_16, arg_vpm, arg_vdw + write_vpm_16 arg_vpm + + mov -, vw_wait + + .rep i, 7 + mov -, sacq(i+9) # Master waits for slave + mov -, srel(i+1) # Master releases slave + .endr + + mov r0, arg_vdw + add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr + + mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) + mov r3, PASS16_STRIDE + + .rep i, 16 + add r0, r0, r2; mov vw_setup, r0 + add r1, r1, r3; mov vw_addr, r1 + .endr + + bra -, ra_link_1 + nop + nop + nop +.endm +.endif + +.if STAGES>18 +.macro body_ra_save_32 + write_vpm_32 + + mov -, vw_wait + + .rep i, 7 + mov -, sacq(i+9) # Master waits for slave + mov -, srel(i+1) # Master releases slave + .endr + + mov r0, ra_vdw_32 + add ra_save_ptr, ra_save_ptr, rb_0x40; mov r1, ra_save_ptr + + mov r2, vdw_setup_0(1, 16, dma_h32(1,0)) - vdw_setup_0(1, 16, dma_h32(0,0)) + mov r3, PASS32_STRIDE + + .rep i, 32 + add r0, r0, r2; mov vw_setup, r0 + add r1, r1, r3; mov vw_addr, r1 + .endr + + bra -, ra_link_1 + nop + nop + nop +.endm +.endif diff --git a/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_trans.qasm b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_trans.qasm new file mode 100755 index 0000000..fdfa869 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_trans.qasm @@ -0,0 +1,133 @@ +# BCM2835 "GPU_FFT" release 2.0 +# +# Copyright (c) 2014, Andrew Holme. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.set rb_offsets_re, rb0 # 8 +.set rb_offsets_im, rb8 # 8 +.set rb_0x10, rb16 +.set rb_X_STRIDE, rb17 +.set rb_Y_STRIDE_SRC, rb18 +.set rb_Y_STRIDE_DST, rb19 +.set rb_NX, rb20 +.set rb_NY, rb21 + +.set ra_x, ra0 +.set ra_y, ra1 +.set ra_src_base, ra2 +.set ra_dst_base, ra3 +.set ra_src_cell, ra4 +.set ra_dst_cell, ra5 +.set ra_vdw_stride, ra6 + + mov t0s, unif # src->vc_msg + ldtmu0 # r4 = vc_unifs + add t0s, r4, 3*4 # 3rd unif + ldtmu0 # r4 = src->in + add ra_src_base, r4, unif # optional offset + + mov t0s, unif # dst->vc_msg + ldtmu0 # r4 = vc_unifs + add t0s, r4, 3*4 # 3rd unif + ldtmu0 # r4 = src->in + add ra_dst_base, r4, unif # optional offset + + mov rb_Y_STRIDE_SRC, unif + mov rb_Y_STRIDE_DST, unif + mov rb_NX, unif + mov rb_NY, unif + + mov rb_X_STRIDE, 2*4 # sizeof complex + mov rb_0x10, 0x10 + + mov r0, vdw_setup_1(0) + add r0, r0, rb_Y_STRIDE_DST + mov r1, 16*4 + sub ra_vdw_stride, r0, r1 + + nop; mul24 r0, elem_num, rb_X_STRIDE +.rep i, 8 + mov rb_offsets_re+i, r0 + add rb_offsets_im+i, r0, 4 + add r0, r0, rb_Y_STRIDE_SRC +.endr + + mov ra_y, 0 +:outer + mov ra_x, 0 +:inner + + nop; mul24 r1, ra_y, rb_Y_STRIDE_SRC + nop; mul24 r0, ra_x, rb_X_STRIDE + add r0, r0, r1 + add ra_src_cell, ra_src_base, r0 + + nop; mul24 r1, ra_x, rb_Y_STRIDE_DST + nop; mul24 r0, ra_y, rb_X_STRIDE + add r0, r0, r1 + add ra_dst_cell, ra_dst_base, r0 + + mov vw_setup, vpm_setup(16, 1, v32(0,0)) + + add t0s, ra_src_cell, rb_offsets_re + add t1s, ra_src_cell, rb_offsets_im + .rep i, 7 + add t0s, ra_src_cell, rb_offsets_re+1+i + add t1s, ra_src_cell, rb_offsets_im+1+i + ldtmu0 + mov vpm, r4 + ldtmu1 + mov vpm, r4 + .endr + ldtmu0 + mov vpm, r4 + ldtmu1 + mov vpm, r4 + + mov vw_setup, vdw_setup_0(16, 16, dma_h32(0,0)) + mov vw_setup, ra_vdw_stride + mov vw_addr, ra_dst_cell + mov -, vw_wait + + add ra_x, ra_x, rb_0x10 + nop + sub.setf -, ra_x, rb_NX + brr.allnz -, r:inner + nop + nop + nop + + add ra_y, ra_y, 8 + nop + sub.setf -, ra_y, rb_NY + brr.allnz -, r:outer + nop + nop + nop + + mov interrupt, 1 + nop; nop; thrend + nop + nop diff --git a/host_applications/linux/apps/hello_pi/hello_font/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_font/CMakeLists.txt new file mode 100755 index 0000000..448d2cf --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_font/CMakeLists.txt @@ -0,0 +1,9 @@ +set(EXEC hello_font.bin) +set(SRCS main.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) +target_link_libraries(${EXEC} vgfont freetype z) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_font/Makefile b/host_applications/linux/apps/hello_pi/hello_font/Makefile new file mode 100755 index 0000000..7ec49f2 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_font/Makefile @@ -0,0 +1,7 @@ +OBJS=main.o +BIN=hello_font.bin + +LDFLAGS+=-lvgfont -lfreetype -lz + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_font/Vera.ttf b/host_applications/linux/apps/hello_pi/hello_font/Vera.ttf new file mode 100755 index 0000000..58cd6b5 Binary files /dev/null and b/host_applications/linux/apps/hello_pi/hello_font/Vera.ttf differ diff --git a/host_applications/linux/apps/hello_pi/hello_font/main.c b/host_applications/linux/apps/hello_pi/hello_font/main.c new file mode 100755 index 0000000..66bbe83 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_font/main.c @@ -0,0 +1,138 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Test app for VG font library. + +#include +#include +#include +#include +#include + +#include "bcm_host.h" +#include "vgfont.h" + +static const char *strnchr(const char *str, size_t len, char c) +{ + const char *e = str + len; + do { + if (*str == c) { + return str; + } + } while (++str < e); + return NULL; +} + +int32_t render_subtitle(GRAPHICS_RESOURCE_HANDLE img, const char *text, const int skip, const uint32_t text_size, const uint32_t y_offset) +{ + uint32_t text_length = strlen(text)-skip; + uint32_t width=0, height=0; + const char *split = text; + int32_t s=0; + int len = 0; // length of pre-subtitle + uint32_t img_w, img_h; + + graphics_get_resource_size(img, &img_w, &img_h); + + if (text_length==0) + return 0; + while (split[0]) { + s = graphics_resource_text_dimensions_ext(img, split, text_length-(split-text), &width, &height, text_size); + if (s != 0) return s; + if (width > img_w) { + const char *space = strnchr(split, text_length-(split-text), ' '); + if (!space) { + len = split+1-text; + split = split+1; + } else { + len = space-text; + split = space+1; + } + } else { + break; + } + } + // split now points to last line of text. split-text = length of initial text. text_length-(split-text) is length of last line + if (width) { + s = graphics_resource_render_text_ext(img, (img_w - width)>>1, y_offset-height, + GRAPHICS_RESOURCE_WIDTH, + GRAPHICS_RESOURCE_HEIGHT, + GRAPHICS_RGBA32(0xff,0xff,0xff,0xff), /* fg */ + GRAPHICS_RGBA32(0,0,0,0x80), /* bg */ + split, text_length-(split-text), text_size); + if (s!=0) return s; + } + return render_subtitle(img, text, skip+text_length-len, text_size, y_offset - height); +} + +int main(void) +{ + GRAPHICS_RESOURCE_HANDLE img; + uint32_t width, height; + int LAYER=1; + bcm_host_init(); + int s; + + s = gx_graphics_init("."); + assert(s == 0); + + s = graphics_get_display_size(0, &width, &height); + assert(s == 0); + + s = gx_create_window(0, width, height, GRAPHICS_RESOURCE_RGBA32, &img); + assert(s == 0); + + // transparent before display to avoid screen flash + graphics_resource_fill(img, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0x00)); + + graphics_display_resource(img, 0, LAYER, 0, 0, GRAPHICS_RESOURCE_WIDTH, GRAPHICS_RESOURCE_HEIGHT, VC_DISPMAN_ROT0, 1); + + uint32_t text_size = 10; + while (1) { + const char *text = "The quick brown fox jumps over the lazy dog"; + uint32_t y_offset = height-60+text_size/2; + graphics_resource_fill(img, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0x00)); + // blue, at the top (y=40) + graphics_resource_fill(img, 0, 40, width, 1, GRAPHICS_RGBA32(0,0,0xff,0xff)); + + // green, at the bottom (y=height-40) + graphics_resource_fill(img, 0, height-40, width, 1, GRAPHICS_RGBA32(0,0xff,0,0xff)); + + // draw the subtitle text + render_subtitle(img, text, 0, text_size, y_offset); + graphics_update_displayed_resource(img, 0, 0, 0, 0); + text_size += 1; + if (text_size > 50) + text_size = 10; + } + + graphics_display_resource(img, 0, LAYER, 0, 0, GRAPHICS_RESOURCE_WIDTH, GRAPHICS_RESOURCE_HEIGHT, VC_DISPMAN_ROT0, 0); + graphics_delete_resource(img); + + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_jpeg/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_jpeg/CMakeLists.txt new file mode 100755 index 0000000..a56dda5 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_jpeg/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_jpeg.bin) +set(SRCS jpeg.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_jpeg/Makefile b/host_applications/linux/apps/hello_pi/hello_jpeg/Makefile new file mode 100755 index 0000000..04fd84f --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_jpeg/Makefile @@ -0,0 +1,6 @@ +OBJS=jpeg.o +BIN=hello_jpeg.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.c b/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.c new file mode 100755 index 0000000..233b20e --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.c @@ -0,0 +1,696 @@ +/* +Copyright (c) 2012, Matt Ownby + Anthong Sale + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include "jpeg.h" + +#define TIMEOUT_MS 2000 + +typedef struct _COMPONENT_DETAILS { + COMPONENT_T *component; + OMX_HANDLETYPE handle; + int inPort; + int outPort; +} COMPONENT_DETAILS; + +struct _OPENMAX_JPEG_DECODER { + ILCLIENT_T *client; + COMPONENT_DETAILS *imageDecoder; + COMPONENT_DETAILS *imageResizer; + OMX_BUFFERHEADERTYPE **ppInputBufferHeader; + int inputBufferHeaderCount; + OMX_BUFFERHEADERTYPE *pOutputBufferHeader; +}; + +int bufferIndex = 0; // index to buffer array + +int +portSettingsChanged(OPENMAX_JPEG_DECODER * decoder) +{ + OMX_PARAM_PORTDEFINITIONTYPE portdef; + + // need to setup the input for the resizer with the output of the + // decoder + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = decoder->imageDecoder->outPort; + OMX_GetParameter(decoder->imageDecoder->handle, + OMX_IndexParamPortDefinition, &portdef); + + unsigned int uWidth = + (unsigned int) portdef.format.image.nFrameWidth; + unsigned int uHeight = + (unsigned int) portdef.format.image.nFrameHeight; + + // tell resizer input what the decoder output will be providing + portdef.nPortIndex = decoder->imageResizer->inPort; + OMX_SetParameter(decoder->imageResizer->handle, + OMX_IndexParamPortDefinition, &portdef); + + // establish tunnel between decoder output and resizer input + OMX_SetupTunnel(decoder->imageDecoder->handle, + decoder->imageDecoder->outPort, + decoder->imageResizer->handle, + decoder->imageResizer->inPort); + + // enable ports + OMX_SendCommand(decoder->imageDecoder->handle, + OMX_CommandPortEnable, + decoder->imageDecoder->outPort, NULL); + OMX_SendCommand(decoder->imageResizer->handle, + OMX_CommandPortEnable, + decoder->imageResizer->inPort, NULL); + + // put resizer in idle state (this allows the outport of the decoder + // to become enabled) + OMX_SendCommand(decoder->imageResizer->handle, + OMX_CommandStateSet, OMX_StateIdle, NULL); + + // wait for state change complete + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, + OMX_CommandStateSet, 1, + OMX_StateIdle, 1, 0, TIMEOUT_MS); + + // once the state changes, both ports should become enabled and the + // resizer + // output should generate a settings changed event + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, + OMX_CommandPortEnable, 1, + decoder->imageDecoder->outPort, 1, 0, + TIMEOUT_MS); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandPortEnable, 1, + decoder->imageResizer->inPort, 1, 0, + TIMEOUT_MS); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventPortSettingsChanged, + decoder->imageResizer->outPort, 1, 0, 1, 0, + TIMEOUT_MS); + + ilclient_disable_port(decoder->imageResizer->component, + decoder->imageResizer->outPort); + + // query output buffer requirements for resizer + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = decoder->imageResizer->outPort; + OMX_GetParameter(decoder->imageResizer->handle, + OMX_IndexParamPortDefinition, &portdef); + + // change output color format and dimensions to match input + portdef.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused; + portdef.format.image.eColorFormat = OMX_COLOR_Format32bitABGR8888; + portdef.format.image.nFrameWidth = uWidth; + portdef.format.image.nFrameHeight = uHeight; + portdef.format.image.nStride = 0; + portdef.format.image.nSliceHeight = 0; + portdef.format.image.bFlagErrorConcealment = OMX_FALSE; + + OMX_SetParameter(decoder->imageResizer->handle, + OMX_IndexParamPortDefinition, &portdef); + + // grab output requirements again to get actual buffer size + // requirement (and buffer count requirement!) + OMX_GetParameter(decoder->imageResizer->handle, + OMX_IndexParamPortDefinition, &portdef); + + // move resizer into executing state + ilclient_change_component_state(decoder->imageResizer->component, + OMX_StateExecuting); + + // show some logging so user knows it's working + printf + ("Width: %u Height: %u Output Color Format: 0x%x Buffer Size: %u\n", + (unsigned int) portdef.format.image.nFrameWidth, + (unsigned int) portdef.format.image.nFrameHeight, + (unsigned int) portdef.format.image.eColorFormat, + (unsigned int) portdef.nBufferSize); + fflush(stdout); + + // enable output port of resizer + OMX_SendCommand(decoder->imageResizer->handle, + OMX_CommandPortEnable, + decoder->imageResizer->outPort, NULL); + + // allocate the buffer + // void* outputBuffer = 0; + // if (posix_memalign(&outputBuffer, portdef.nBufferAlignment, + // portdef.nBufferSize) != 0) + // { + // perror("Allocating output buffer"); + // return OMXJPEG_ERROR_MEMORY; + // } + + // set the buffer + // int ret = OMX_UseBuffer(decoder->imageResizer->handle, + // &decoder->pOutputBufferHeader, + // decoder->imageResizer->outPort, NULL, + // portdef.nBufferSize, + // (OMX_U8 *) outputBuffer); + int ret = OMX_AllocateBuffer(decoder->imageResizer->handle, + &decoder->pOutputBufferHeader, + decoder->imageResizer-> + outPort, + NULL, + portdef.nBufferSize); + if (ret != OMX_ErrorNone) { + perror("Eror allocating buffer"); + fprintf(stderr, "OMX_AllocateBuffer returned 0x%x allocating buffer size 0x%x\n", ret, portdef.nBufferSize); + return OMXJPEG_ERROR_MEMORY; + } + + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, + OMX_CommandPortEnable, 1, + decoder->imageResizer->outPort, 1, 0, + TIMEOUT_MS); + + return OMXJPEG_OK; +} + +int +portSettingsChangedAgain(OPENMAX_JPEG_DECODER * decoder) +{ + ilclient_disable_port(decoder->imageDecoder->component, + decoder->imageDecoder->outPort); + ilclient_disable_port(decoder->imageResizer->component, + decoder->imageResizer->inPort); + + OMX_PARAM_PORTDEFINITIONTYPE portdef; + + // need to setup the input for the resizer with the output of the + // decoder + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = decoder->imageDecoder->outPort; + OMX_GetParameter(decoder->imageDecoder->handle, + OMX_IndexParamPortDefinition, &portdef); + + // tell resizer input what the decoder output will be providing + portdef.nPortIndex = decoder->imageResizer->inPort; + OMX_SetParameter(decoder->imageResizer->handle, + OMX_IndexParamPortDefinition, &portdef); + + // enable output of decoder and input of resizer (ie enable tunnel) + ilclient_enable_port(decoder->imageDecoder->component, + decoder->imageDecoder->outPort); + ilclient_enable_port(decoder->imageResizer->component, + decoder->imageResizer->inPort); + + // need to wait for this event + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventPortSettingsChanged, + decoder->imageResizer->outPort, 1, + 0, 0, 0, TIMEOUT_MS); + + return OMXJPEG_OK; +} + +int +prepareResizer(OPENMAX_JPEG_DECODER * decoder) +{ + decoder->imageResizer = malloc(sizeof(COMPONENT_DETAILS)); + if (decoder->imageResizer == NULL) { + perror("malloc image resizer"); + return OMXJPEG_ERROR_MEMORY; + } + + int ret = ilclient_create_component(decoder->client, + &decoder-> + imageResizer-> + component, + "resize", + ILCLIENT_DISABLE_ALL_PORTS + | + ILCLIENT_ENABLE_INPUT_BUFFERS + | + ILCLIENT_ENABLE_OUTPUT_BUFFERS); + if (ret != 0) { + perror("image resizer"); + return OMXJPEG_ERROR_CREATING_COMP; + } + // grab the handle for later use + decoder->imageResizer->handle = + ILC_GET_HANDLE(decoder->imageResizer->component); + + // get and store the ports + OMX_PORT_PARAM_TYPE port; + port.nSize = sizeof(OMX_PORT_PARAM_TYPE); + port.nVersion.nVersion = OMX_VERSION; + + OMX_GetParameter(ILC_GET_HANDLE(decoder->imageResizer->component), + OMX_IndexParamImageInit, &port); + if (port.nPorts != 2) { + return OMXJPEG_ERROR_WRONG_NO_PORTS; + } + decoder->imageResizer->inPort = port.nStartPortNumber; + decoder->imageResizer->outPort = port.nStartPortNumber + 1; + + decoder->pOutputBufferHeader = NULL; + + return OMXJPEG_OK; +} + +int +prepareImageDecoder(OPENMAX_JPEG_DECODER * decoder) +{ + decoder->imageDecoder = malloc(sizeof(COMPONENT_DETAILS)); + if (decoder->imageDecoder == NULL) { + perror("malloc image decoder"); + return OMXJPEG_ERROR_MEMORY; + } + + int ret = ilclient_create_component(decoder->client, + &decoder-> + imageDecoder-> + component, + "image_decode", + ILCLIENT_DISABLE_ALL_PORTS + | + ILCLIENT_ENABLE_INPUT_BUFFERS); + + if (ret != 0) { + perror("image decode"); + return OMXJPEG_ERROR_CREATING_COMP; + } + // grab the handle for later use in OMX calls directly + decoder->imageDecoder->handle = + ILC_GET_HANDLE(decoder->imageDecoder->component); + + // get and store the ports + OMX_PORT_PARAM_TYPE port; + port.nSize = sizeof(OMX_PORT_PARAM_TYPE); + port.nVersion.nVersion = OMX_VERSION; + + OMX_GetParameter(decoder->imageDecoder->handle, + OMX_IndexParamImageInit, &port); + if (port.nPorts != 2) { + return OMXJPEG_ERROR_WRONG_NO_PORTS; + } + decoder->imageDecoder->inPort = port.nStartPortNumber; + decoder->imageDecoder->outPort = port.nStartPortNumber + 1; + + return OMXJPEG_OK; +} + +int +startupImageDecoder(OPENMAX_JPEG_DECODER * decoder) +{ + // move to idle + ilclient_change_component_state(decoder->imageDecoder->component, + OMX_StateIdle); + + // set input image format + OMX_IMAGE_PARAM_PORTFORMATTYPE imagePortFormat; + memset(&imagePortFormat, 0, sizeof(OMX_IMAGE_PARAM_PORTFORMATTYPE)); + imagePortFormat.nSize = sizeof(OMX_IMAGE_PARAM_PORTFORMATTYPE); + imagePortFormat.nVersion.nVersion = OMX_VERSION; + imagePortFormat.nPortIndex = decoder->imageDecoder->inPort; + imagePortFormat.eCompressionFormat = OMX_IMAGE_CodingJPEG; + OMX_SetParameter(decoder->imageDecoder->handle, + OMX_IndexParamImagePortFormat, &imagePortFormat); + + // get buffer requirements + OMX_PARAM_PORTDEFINITIONTYPE portdef; + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = decoder->imageDecoder->inPort; + OMX_GetParameter(decoder->imageDecoder->handle, + OMX_IndexParamPortDefinition, &portdef); + + // enable the port and setup the buffers + OMX_SendCommand(decoder->imageDecoder->handle, + OMX_CommandPortEnable, + decoder->imageDecoder->inPort, NULL); + decoder->inputBufferHeaderCount = portdef.nBufferCountActual; + // allocate pointer array + decoder->ppInputBufferHeader = + (OMX_BUFFERHEADERTYPE **) malloc(sizeof(void) * + decoder->inputBufferHeaderCount); + // allocate each buffer + int i; + for (i = 0; i < decoder->inputBufferHeaderCount; i++) { + if (OMX_AllocateBuffer(decoder->imageDecoder->handle, + &decoder->ppInputBufferHeader[i], + decoder->imageDecoder->inPort, + (void *) i, + portdef.nBufferSize) != OMX_ErrorNone) { + perror("Allocate decode buffer"); + return OMXJPEG_ERROR_MEMORY; + } + } + // wait for port enable to complete - which it should once buffers are + // assigned + int ret = + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, + OMX_CommandPortEnable, 0, + decoder->imageDecoder->inPort, 0, + 0, TIMEOUT_MS); + if (ret != 0) { + fprintf(stderr, "Did not get port enable %d\n", ret); + return OMXJPEG_ERROR_EXECUTING; + } + // start executing the decoder + ret = OMX_SendCommand(decoder->imageDecoder->handle, + OMX_CommandStateSet, OMX_StateExecuting, NULL); + if (ret != 0) { + fprintf(stderr, "Error starting image decoder %x\n", ret); + return OMXJPEG_ERROR_EXECUTING; + } + ret = ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, + OMX_StateExecuting, 0, 0, 1, 0, + TIMEOUT_MS); + if (ret != 0) { + fprintf(stderr, "Did not receive executing stat %d\n", ret); + // return OMXJPEG_ERROR_EXECUTING; + } + + return OMXJPEG_OK; +} + +// this function run the boilerplate to setup the openmax components; +int +setupOpenMaxJpegDecoder(OPENMAX_JPEG_DECODER ** pDecoder) +{ + *pDecoder = malloc(sizeof(OPENMAX_JPEG_DECODER)); + if (pDecoder[0] == NULL) { + perror("malloc decoder"); + return OMXJPEG_ERROR_MEMORY; + } + memset(*pDecoder, 0, sizeof(OPENMAX_JPEG_DECODER)); + + if ((pDecoder[0]->client = ilclient_init()) == NULL) { + perror("ilclient_init"); + return OMXJPEG_ERROR_ILCLIENT_INIT; + } + + if (OMX_Init() != OMX_ErrorNone) { + ilclient_destroy(pDecoder[0]->client); + perror("OMX_Init"); + return OMXJPEG_ERROR_OMX_INIT; + } + // prepare the image decoder + int ret = prepareImageDecoder(pDecoder[0]); + if (ret != OMXJPEG_OK) + return ret; + + ret = prepareResizer(pDecoder[0]); + if (ret != OMXJPEG_OK) + return ret; + + ret = startupImageDecoder(pDecoder[0]); + if (ret != OMXJPEG_OK) + return ret; + + return OMXJPEG_OK; +} + +// this function passed the jpeg image buffer in, and returns the decoded +// image +int +decodeImage(OPENMAX_JPEG_DECODER * decoder, char *sourceImage, + size_t imageSize) +{ + char *sourceOffset = sourceImage; // we store a separate + // buffer ot image so we + // can offset it + size_t toread = 0; // bytes left to read from buffer + toread += imageSize; + int bFilled = 0; // have we filled our output + // buffer + bufferIndex = 0; + + while (toread > 0) { + // get next buffer from array + OMX_BUFFERHEADERTYPE *pBufHeader = + decoder->ppInputBufferHeader[bufferIndex]; + + // step index and reset to 0 if required + bufferIndex++; + if (bufferIndex >= decoder->inputBufferHeaderCount) + bufferIndex = 0; + + // work out the next chunk to load into the decoder + if (toread > pBufHeader->nAllocLen) + pBufHeader->nFilledLen = pBufHeader->nAllocLen; + else + pBufHeader->nFilledLen = toread; + + toread = toread - pBufHeader->nFilledLen; + + // pass the bytes to the buffer + memcpy(pBufHeader->pBuffer, sourceOffset, pBufHeader->nFilledLen); + + // update the buffer pointer and set the input flags + + sourceOffset = sourceOffset + pBufHeader->nFilledLen; + pBufHeader->nOffset = 0; + pBufHeader->nFlags = 0; + if (toread <= 0) { + pBufHeader->nFlags = OMX_BUFFERFLAG_EOS; + } + // empty the current buffer + int ret = + OMX_EmptyThisBuffer(decoder->imageDecoder->handle, + pBufHeader); + + if (ret != OMX_ErrorNone) { + perror("Empty input buffer"); + fprintf(stderr, "return code %x\n", ret); + return OMXJPEG_ERROR_MEMORY; + } + // wait for buffer to empty or port changed event + int done = 0; + while ((done == 0) && (decoder->pOutputBufferHeader == NULL)) { + if (decoder->pOutputBufferHeader == NULL) { + ret = + ilclient_wait_for_event + (decoder->imageDecoder->component, + OMX_EventPortSettingsChanged, + decoder->imageDecoder->outPort, 0, 0, 1, 0, 5); + + if (ret == 0) { + ret = portSettingsChanged(decoder); + if (ret != OMXJPEG_OK) + return ret; + } + } else { + ret = + ilclient_remove_event(decoder->imageDecoder->component, + OMX_EventPortSettingsChanged, + decoder->imageDecoder->outPort, + 0, 0, 1); + if (ret == 0) + portSettingsChangedAgain(decoder); + + } + + // check to see if buffer is now empty + if (pBufHeader->nFilledLen == 0) + done = 1; + + if ((done == 0) + || (decoder->pOutputBufferHeader == NULL)) + sleep(1); + } + + // fill the buffer if we have created the buffer + if ((bFilled == 0) && (decoder->pOutputBufferHeader != NULL)) { + ret = OMX_FillThisBuffer(decoder->imageResizer->handle, + decoder->pOutputBufferHeader); + if (ret != OMX_ErrorNone) { + perror("Filling output buffer"); + fprintf(stderr, "Error code %x\n", ret); + return OMXJPEG_ERROR_MEMORY; + } + + bFilled = 1; + } + } + + // wait for buffer to fill + /* + * while(pBufHeader->nFilledLen == 0) { sleep(5); } + */ + + // wait for end of stream events + int ret = + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventBufferFlag, + decoder->imageDecoder->outPort, 1, + OMX_BUFFERFLAG_EOS, 1, + 0, 2); + if (ret != 0) { + fprintf(stderr, "No EOS event on image decoder %d\n", ret); + } + ret = ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventBufferFlag, + decoder->imageResizer->outPort, 1, + OMX_BUFFERFLAG_EOS, 1, 0, 2); + if (ret != 0) { + fprintf(stderr, "No EOS event on image resizer %d\n", ret); + } + return OMXJPEG_OK; +} + +// this function cleans up the decoder. +void +cleanup(OPENMAX_JPEG_DECODER * decoder) +{ + // flush everything through + OMX_SendCommand(decoder->imageDecoder->handle, + OMX_CommandFlush, decoder->imageDecoder->outPort, + NULL); + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, OMX_CommandFlush, 0, + decoder->imageDecoder->outPort, 0, 0, + TIMEOUT_MS); + OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandFlush, + decoder->imageResizer->inPort, NULL); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandFlush, 0, + decoder->imageResizer->inPort, 1, 0, + TIMEOUT_MS); + + OMX_SendCommand(decoder->imageDecoder->handle, OMX_CommandPortDisable, + decoder->imageDecoder->inPort, NULL); + + int i = 0; + for (i = 0; i < decoder->inputBufferHeaderCount; i++) { + OMX_BUFFERHEADERTYPE *vpBufHeader = + decoder->ppInputBufferHeader[i]; + + OMX_FreeBuffer(decoder->imageDecoder->handle, + decoder->imageDecoder->inPort, vpBufHeader); + } + + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, OMX_CommandPortDisable, + 0, decoder->imageDecoder->inPort, 0, 0, + TIMEOUT_MS); + + OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandPortDisable, + decoder->imageResizer->outPort, NULL); + + OMX_FreeBuffer(decoder->imageResizer->handle, + decoder->imageResizer->outPort, + decoder->pOutputBufferHeader); + + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandPortDisable, + 0, decoder->imageResizer->outPort, 0, 0, + TIMEOUT_MS); + + OMX_SendCommand(decoder->imageDecoder->handle, OMX_CommandPortDisable, + decoder->imageDecoder->outPort, NULL); + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, OMX_CommandPortDisable, + 0, decoder->imageDecoder->outPort, 0, 0, + TIMEOUT_MS); + + OMX_SendCommand(decoder->imageResizer->handle, OMX_CommandPortDisable, + decoder->imageResizer->inPort, NULL); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandPortDisable, + 0, decoder->imageResizer->inPort, 0, 0, + TIMEOUT_MS); + + OMX_SetupTunnel(decoder->imageDecoder->handle, + decoder->imageDecoder->outPort, NULL, 0); + OMX_SetupTunnel(decoder->imageResizer->handle, + decoder->imageResizer->inPort, NULL, 0); + + ilclient_change_component_state(decoder->imageDecoder->component, + OMX_StateIdle); + ilclient_change_component_state(decoder->imageResizer->component, + OMX_StateIdle); + + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, OMX_CommandStateSet, 0, + OMX_StateIdle, 0, 0, TIMEOUT_MS); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandStateSet, 0, + OMX_StateIdle, 0, 0, TIMEOUT_MS); + + ilclient_change_component_state(decoder->imageDecoder->component, + OMX_StateLoaded); + ilclient_change_component_state(decoder->imageResizer->component, + OMX_StateLoaded); + + ilclient_wait_for_event(decoder->imageDecoder->component, + OMX_EventCmdComplete, OMX_CommandStateSet, 0, + OMX_StateLoaded, 0, 0, TIMEOUT_MS); + ilclient_wait_for_event(decoder->imageResizer->component, + OMX_EventCmdComplete, OMX_CommandStateSet, 0, + OMX_StateLoaded, 0, 0, TIMEOUT_MS); + + OMX_Deinit(); + + if (decoder->client != NULL) { + ilclient_destroy(decoder->client); + } +} + +int +main(int argc, char *argv[]) +{ + OPENMAX_JPEG_DECODER *pDecoder; + char *sourceImage; + size_t imageSize; + int s; + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + return -1; + } + FILE *fp = fopen(argv[1], "rb"); + if (!fp) { + printf("File %s not found.\n", argv[1]); + } + fseek(fp, 0L, SEEK_END); + imageSize = ftell(fp); + fseek(fp, 0L, SEEK_SET); + sourceImage = malloc(imageSize); + assert(sourceImage != NULL); + s = fread(sourceImage, 1, imageSize, fp); + assert(s == imageSize); + fclose(fp); + bcm_host_init(); + s = setupOpenMaxJpegDecoder(&pDecoder); + assert(s == 0); + s = decodeImage(pDecoder, sourceImage, imageSize); + assert(s == 0); + cleanup(pDecoder); + free(sourceImage); + return 0; +} diff --git a/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.h b/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.h new file mode 100755 index 0000000..f525dab --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.h @@ -0,0 +1,68 @@ +/* +Copyright (c) 2012, Matt Ownby + Anthong Sale + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _OPTION_H_ +#define _OPTION_H_ + +/* +Defines the methods for interacting with openmax il and ilclient to decode +jpeg images from the camera +*/ + +#include +#include +#include +#include + +#include "bcm_host.h" +#include "ilclient.h" + +#define OMXJPEG_OK 0 +#define OMXJPEG_ERROR_ILCLIENT_INIT -1024 +#define OMXJPEG_ERROR_OMX_INIT -1025 +#define OMXJPEG_ERROR_MEMORY -1026 +#define OMXJPEG_ERROR_CREATING_COMP -1027 +#define OMXJPEG_ERROR_WRONG_NO_PORTS -1028 +#define OMXJPEG_ERROR_EXECUTING -1029 +#define OMXJPEG_ERROR_NOSETTINGS -1030 + +typedef struct _OPENMAX_JPEG_DECODER OPENMAX_JPEG_DECODER; + +//this function run the boilerplate to setup the openmax components; +int setupOpenMaxJpegDecoder(OPENMAX_JPEG_DECODER** decoder); + +//this function passed the jpeg image buffer in, and returns the decoded image +int decodeImage(OPENMAX_JPEG_DECODER* decoder, + char* sourceImage, size_t imageSize); + +//this function cleans up the decoder. +void cleanup(OPENMAX_JPEG_DECODER* decoder); + +#endif + diff --git a/host_applications/linux/apps/hello_pi/hello_mmal_encode/Makefile b/host_applications/linux/apps/hello_pi/hello_mmal_encode/Makefile new file mode 100755 index 0000000..98f83f6 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_mmal_encode/Makefile @@ -0,0 +1,6 @@ +OBJS=mmal_encode.o +BIN=hello_mmal_encode.bin +LDFLAGS+=-lmmal -lmmal_core -lmmal_components -lmmal_util -lmmal_vc_client + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_mmal_encode/mmal_encode.c b/host_applications/linux/apps/hello_pi/hello_mmal_encode/mmal_encode.c new file mode 100755 index 0000000..21ebf92 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_mmal_encode/mmal_encode.c @@ -0,0 +1,261 @@ +/* +Copyright (C) 2016 RealVNC Limited. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Image encoding example using MMAL + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// Format for test image +const unsigned int WIDTH = 512; +const unsigned int HEIGHT = 512; +const MMAL_FOURCC_T INPUT_ENC = MMAL_ENCODING_RGBA; +const unsigned int BYTESPP = 4; +const uint32_t RED = 0xff; +const uint32_t GREEN = 0xff << 8; +const uint32_t BLUE = 0xff << 16; +const uint32_t ALPHA = 0xff << 24; + +static MMAL_WRAPPER_T* encoder; +static VCOS_SEMAPHORE_T sem; + +// This callback and the above semaphore is used when waiting for +// data to be returned. +static void mmalCallback(MMAL_WRAPPER_T* encoder) +{ + vcos_semaphore_post(&sem); +} + +// Create a test input image in the supplied buffer consisting of 8 vertical bars of +// white | black | red | green | blue | cyan | magenta | yellow +void create_rgba_test_image(void* buf, unsigned int length, unsigned int stride) +{ + uint32_t* pixel = buf; + int i; + for (i=0; iinput[0]; + encoder->status = MMAL_SUCCESS; + + if (portIn->is_enabled) { + if (mmal_wrapper_port_disable(portIn) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to disable input port\n"); + exit(1); + } + } + + portIn->format->encoding = INPUT_ENC; + portIn->format->es->video.width = VCOS_ALIGN_UP(WIDTH, 32); + portIn->format->es->video.height = VCOS_ALIGN_UP(HEIGHT, 16); + portIn->format->es->video.crop.x = 0; + portIn->format->es->video.crop.y = 0; + portIn->format->es->video.crop.width = WIDTH; + portIn->format->es->video.crop.height = HEIGHT; + if (mmal_port_format_commit(portIn) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to commit input port format\n"); + exit(1); + } + + portIn->buffer_size = portIn->buffer_size_recommended; + portIn->buffer_num = portIn->buffer_num_recommended; + + if (mmal_wrapper_port_enable(portIn, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE) + != MMAL_SUCCESS) { + fprintf(stderr, "Failed to enable input port\n"); + exit(1); + } + + printf("- input %4.4s %ux%u\n", + (char*)&portIn->format->encoding, + portIn->format->es->video.width, portIn->format->es->video.height); + + // Configure output + + portOut = encoder->output[0]; + + if (portOut->is_enabled) { + if (mmal_wrapper_port_disable(portOut) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to disable output port\n"); + exit(1); + } + } + + portOut->format->encoding = encoding; + if (mmal_port_format_commit(portOut) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to commit output port format\n"); + exit(1); + } + + mmal_port_parameter_set_uint32(portOut, MMAL_PARAMETER_JPEG_Q_FACTOR, 100); + + portOut->buffer_size = portOut->buffer_size_recommended; + portOut->buffer_num = portOut->buffer_num_recommended; + + if (mmal_wrapper_port_enable(portOut, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE) + != MMAL_SUCCESS) { + fprintf(stderr, "Failed to enable output port\n"); + exit(1); + } + + printf("- output %4.4s\n", (char*)&encoding); + + // Perform the encoding + + outFile = fopen(filename, "w"); + if (!outFile) { + fprintf(stderr, "Failed to open file %s (%s)\n", filename, strerror(errno)); + exit(1); + } + + while (!eos) { + + // Send output buffers to be filled with encoded image. + while (mmal_wrapper_buffer_get_empty(portOut, &out, 0) == MMAL_SUCCESS) { + if (mmal_port_send_buffer(portOut, out) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to send buffer\n"); + break; + } + } + + // Send image to be encoded. + if (!sent && mmal_wrapper_buffer_get_empty(portIn, &in, 0) == MMAL_SUCCESS) { + printf("- sending %u bytes to encoder\n", in->alloc_size); + create_rgba_test_image(in->data, in->alloc_size, portIn->format->es->video.width); + in->length = in->alloc_size; + in->flags = MMAL_BUFFER_HEADER_FLAG_EOS; + if (mmal_port_send_buffer(portIn, in) != MMAL_SUCCESS) { + fprintf(stderr, "Failed to send buffer\n"); + break; + } + sent = 1; + } + + // Get filled output buffers. + status = mmal_wrapper_buffer_get_full(portOut, &out, 0); + if (status == MMAL_EAGAIN) { + // No buffer available, wait for callback and loop. + vcos_semaphore_wait(&sem); + continue; + } else if (status != MMAL_SUCCESS) { + fprintf(stderr, "Failed to get full buffer\n"); + exit(1); + } + + printf("- received %i bytes\n", out->length); + eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS; + + nw = fwrite(out->data, 1, out->length, outFile); + if (nw != out->length) { + fprintf(stderr, "Failed to write complete buffer\n"); + exit(1); + } + outputWritten += nw; + + mmal_buffer_header_release(out); + } + + mmal_port_flush(portOut); + + fclose(outFile); + printf("- written %u bytes to %s\n\n", outputWritten, filename); +} + + +int main(int argc, const char** argv) +{ + bcm_host_init(); + + if (vcos_semaphore_create(&sem, "encoder sem", 0) != VCOS_SUCCESS) { + fprintf(stderr, "Failed to create semaphore\n"); + exit(1); + } + + if (mmal_wrapper_create(&encoder, MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER) + != MMAL_SUCCESS) { + fprintf(stderr, "Failed to create mmal component\n"); + exit(1); + } + encoder->callback = mmalCallback; + + // Perform test encodings in various formats + mmal_encode_test(MMAL_ENCODING_PNG, "out.png"); + mmal_encode_test(MMAL_ENCODING_JPEG, "out.jpg"); + mmal_encode_test(MMAL_ENCODING_GIF, "out.gif"); + mmal_encode_test(MMAL_ENCODING_BMP, "out.bmp"); + + mmal_wrapper_destroy(encoder); + vcos_semaphore_delete(&sem); + + return 0; +} + + diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_teapot/CMakeLists.txt new file mode 100755 index 0000000..cdb8413 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_teapot.bin) +set(SRCS triangle.c video.c models.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/Makefile b/host_applications/linux/apps/hello_pi/hello_teapot/Makefile new file mode 100755 index 0000000..0ebb234 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/Makefile @@ -0,0 +1,7 @@ +OBJS=triangle.o video.o models.o +BIN=hello_teapot.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + + diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/README.md b/host_applications/linux/apps/hello_pi/hello_teapot/README.md new file mode 100755 index 0000000..36fafcf --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/README.md @@ -0,0 +1,4 @@ +hello_videocube +=============== + +Sample for Raspberry Pi that uses egl_render to display video on an animated cube. \ No newline at end of file diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/cube_texture_and_coords.h b/host_applications/linux/apps/hello_pi/hello_teapot/cube_texture_and_coords.h new file mode 100755 index 0000000..7dd30a9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/cube_texture_and_coords.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Spatial coordinates for the cube + +static const GLbyte quadx[6*4*3] = { + /* FRONT */ + -10, -10, 10, + 10, -10, 10, + -10, 10, 10, + 10, 10, 10, + + /* BACK */ + -10, -10, -10, + -10, 10, -10, + 10, -10, -10, + 10, 10, -10, + + /* LEFT */ + -10, -10, 10, + -10, 10, 10, + -10, -10, -10, + -10, 10, -10, + + /* RIGHT */ + 10, -10, -10, + 10, 10, -10, + 10, -10, 10, + 10, 10, 10, + + /* TOP */ + -10, 10, 10, + 10, 10, 10, + -10, 10, -10, + 10, 10, -10, + + /* BOTTOM */ + -10, -10, 10, + -10, -10, -10, + 10, -10, 10, + 10, -10, -10, +}; + +/** Texture coordinates for the quad. */ +static const GLfloat texCoords[6 * 4 * 2] = { + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f +}; + diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/models.c b/host_applications/linux/apps/hello_pi/hello_teapot/models.c new file mode 100755 index 0000000..a5af141 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/models.c @@ -0,0 +1,515 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "GLES/gl.h" +#include "EGL/egl.h" +#include "EGL/eglext.h" +#include "models.h" + +#define VMCS_RESOURCE(a,b) (b) + +/****************************************************************************** +Private typedefs, macros and constants +******************************************************************************/ + +enum {VBO_VERTEX, VBO_NORMAL, VBO_TEXTURE, VBO_MAX}; +#define MAX_MATERIALS 4 +#define MAX_MATERIAL_NAME 32 + +typedef struct wavefront_material_s { + GLuint vbo[VBO_MAX]; + int numverts; + char name[MAX_MATERIAL_NAME]; + GLuint texture; +} WAVEFRONT_MATERIAL_T; + +typedef struct wavefront_model_s { + WAVEFRONT_MATERIAL_T material[MAX_MATERIALS]; + int num_materials; + GLuint texture; +} WAVEFRONT_MODEL_T; + + +/****************************************************************************** +Static Data +******************************************************************************/ + +/****************************************************************************** +Static Function Declarations +******************************************************************************/ + +/****************************************************************************** +Static Function Definitions +******************************************************************************/ + +static void create_vbo(GLenum type, GLuint *vbo, int size, void *data) +{ + glGenBuffers(1, vbo); + vc_assert(*vbo); + glBindBuffer(type, *vbo); + glBufferData(type, size, data, GL_STATIC_DRAW); + glBindBuffer(type, 0); +} + + +static void destroy_vbo(GLuint *vbo) +{ + glDeleteBuffers(1, vbo); + *vbo = 0; +} + +#define MAX_VERTICES 100000 +static void *allocbuffer(int size) +{ + return malloc(size); +} + +static void freebuffer(void *p) +{ + free (p); +} + +static void centre_and_rescale(float *verts, int numvertices) +{ + float cx=0.0f, cy=0.0f, cz=0.0f, scale=0.0f; + float minx=0.0f, miny=0.0f, minz=0.0f; + float maxx=0.0f, maxy=0.0f, maxz=0.0f; + int i; + float *v = verts; + minx = maxx = verts[0]; + miny = maxy = verts[1]; + minz = maxz = verts[2]; + for (i=0; i= 3) *dst++ = src[ind + 2]; + indexes += 3; + } +} + +int draw_wavefront(MODEL_T m, GLuint texture) +{ + int i; + WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; + + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + if (mat->texture == -1) continue; + glBindTexture(GL_TEXTURE_2D, mat->texture ? mat->texture:texture); + if (mat->vbo[VBO_VERTEX]) { + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_VERTEX]); + glVertexPointer(3, GL_FLOAT, 0, NULL); + } + if (mat->vbo[VBO_NORMAL]) { + glEnableClientState(GL_NORMAL_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_NORMAL]); + glNormalPointer(GL_FLOAT, 0, NULL); + } else { + glDisableClientState(GL_NORMAL_ARRAY); + } + if (mat->vbo[VBO_TEXTURE]) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_TEXTURE]); + glTexCoordPointer(2, GL_FLOAT, 0, NULL); + } else { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + glDrawArrays(GL_TRIANGLES, 0, mat->numverts); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + return 0; +} + +struct wavefront_model_loading_s { + unsigned short material_index[MAX_MATERIALS]; + int num_materials; + int numv, numt, numn, numf; + unsigned int data[0]; +}; + +static int load_wavefront_obj(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) +{ + char line[256+1]; + unsigned short pp[54+1]; + FILE *fp; + int i, valid; + float *qv = (float *)m->data; + float *qt = (float *)m->data + 3 * MAX_VERTICES; + float *qn = (float *)m->data + (3+2) * MAX_VERTICES; + unsigned short *qf = (unsigned short *)((float *)m->data + (3+2+3) * MAX_VERTICES); + float *pv = qv, *pt = qt, *pn = qn; + unsigned short *pf = qf; + fp = fopen(modelname, "r"); + if (!fp) return -1; + + m->num_materials = 0; + m->material_index[0] = 0; + + valid = fread(line, 1, sizeof(line)-1, fp); + + while (valid > 0) { + char *s, *end = line; + + while((end-line < valid) && *end != '\n' && *end != '\r') + end++; + *end++ = 0; + + if((end-line < valid) && *end != '\n' && *end != '\r') + *end++ = 0; + + s = line; + + if (s[strlen(s)-1] == 10) s[strlen(s)-1]=0; + switch (s[0]) { + case '#': break; // comment + case '\r': case '\n': case '\0': break; // blank line + case 'm': vc_assert(strncmp(s, "mtllib", sizeof "mtllib"-1)==0); break; + case 'o': break; + case 'u': + if (sscanf(s, "usemtl %s", /*MAX_MATERIAL_NAME-1, */model->material[m->num_materials].name) == 1) { + if (m->num_materials < MAX_MATERIALS) { + if (m->num_materials > 0 && ((pf-qf)/3 == m->material_index[m->num_materials-1] || strcmp(model->material[m->num_materials-1].name, model->material[m->num_materials].name)==0)) { + strcpy(model->material[m->num_materials-1].name, model->material[m->num_materials].name); + m->num_materials--; + } else + m->material_index[m->num_materials] = (pf-qf)/3; + m->num_materials++; + } + } else { printf(s); vc_assert(0); } + break; + case 'g': vc_assert(strncmp(s, "g ", sizeof "g "-1)==0); break; + case 's': vc_assert(strncmp(s, "s ", sizeof "s "-1)==0); break; + case 'v': case 'f': + if (sscanf(s, "v %f %f %f", pv+0, pv+1, pv+2) == 3) { + pv += 3; + } else if (sscanf(s, "vt %f %f", pt+0, pt+1) == 2) { + pt += 2; + } else if (sscanf(s, "vn %f %f %f", pn+0, pn+1, pn+2) == 3) { + pn += 3; + } else if (i = sscanf(s, "f"" %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" + " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" + " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu", + pp+ 0, pp+ 1, pp+ 2, pp+ 3, pp+ 4, pp+ 5, pp+ 6, pp+ 7, pp+ 8, pp+ 9, pp+10, pp+11, + pp+12, pp+13, pp+14, pp+15, pp+16, pp+17, pp+18, pp+19, pp+20, pp+21, pp+22, pp+23, + pp+24, pp+25, pp+26, pp+27, pp+28, pp+29, pp+30, pp+32, pp+32, pp+33, pp+34, pp+35, pp+36), i >= 6) { + int poly = i/2; + //vc_assert(i < countof(pp)); // may need to increment poly count and pp array + for (i=1; i= 6) { + int poly = i/2; + //vc_assert(i < countof(pp); // may need to increment poly count and pp array + for (i=1; i= 9) { + int poly = i/3; + //vc_assert(i < countof(pp); // may need to increment poly count and pp array + for (i=1; i valid ? valid : end-line; + memmove(line, end, valid - i); + valid -= i; + valid += fread(line+valid, 1, sizeof(line)-1-valid, fp); + } + fclose(fp); + + if (m->num_materials==0) m->material_index[m->num_materials++] = 0; + + centre_and_rescale(qv, (pv-qv)/3); + renormalise(qn, (pn-qn)/3); + //centre_and_rescale2(qt, (pt-qt)/2); + + m->numv = pv-qv; + m->numt = pt-qt; + m->numn = pn-qn; + m->numf = pf-qf; + + // compress array + //memcpy((float *)m->data, (float *)m->data, m->numv * sizeof *qv); - nop + memcpy((float *)m->data + m->numv, (float *)m->data + 3 * MAX_VERTICES, m->numt * sizeof *qt); + memcpy((float *)m->data + m->numv + m->numt,(float *) m->data + (3 + 2) * MAX_VERTICES, m->numn * sizeof *qn); + memcpy((float *)m->data + m->numv + m->numt + m->numn, (float *)m->data + (3 + 2 + 3) * MAX_VERTICES, m->numf * sizeof *qf); + + return 0; +} + +static int load_wavefront_dat(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) +{ + FILE *fp; + int s; + const int size = sizeof *m + + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*MAX_VERTICES; //each face has 9 vertices + + fp = fopen(modelname, "r"); + if (!fp) return -1; + s = fread(m, 1, size, fp); + if (s < 0) return -1; + fclose(fp); + return 0; +} + +MODEL_T load_wavefront(const char *modelname, const char *texturename) +{ + WAVEFRONT_MODEL_T *model; + float *temp, *qv, *qt, *qn; + unsigned short *qf; + int i; + int numverts = 0, offset = 0; + struct wavefront_model_loading_s *m; + int s=-1; + char modelname_obj[128]; + model = malloc(sizeof *model); + if (!model || !modelname) return NULL; + memset (model, 0, sizeof *model); + model->texture = 0; //load_texture(texturename); + m = allocbuffer(sizeof *m + + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*MAX_VERTICES); //each face has 9 vertices + if (!m) return 0; + + if (strlen(modelname) + 5 <= sizeof modelname_obj) { + strcpy(modelname_obj, modelname); + strcat(modelname_obj, ".dat"); + s = load_wavefront_dat(modelname_obj, model, m); + } + if (s==0) {} + else if (strncmp(modelname + strlen(modelname) - 4, ".obj", 4) == 0) { + #ifdef DUMP_OBJ_DAT + int size; + FILE *fp; + #endif + s = load_wavefront_obj(modelname, model, m); + #ifdef DUMP_OBJ_DAT + strcpy(modelname_obj, modelname); + strcat(modelname_obj, ".dat"); + size = sizeof *m + + sizeof(float)*(3*m->numv+2*m->numt+3*m->numn) + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*m->numf; //each face has 9 vertices + fp = host_file_open(modelname_obj, "w"); + fwrite(m, 1, size, fp); + fclose(fp); + #endif + } else if (strncmp(modelname + strlen(modelname) - 4, ".dat", 4) == 0) { + s = load_wavefront_dat(modelname, model, m); + } + if (s != 0) return 0; + + qv = (float *)(m->data); + qt = (float *)(m->data + m->numv); + qn = (float *)(m->data + m->numv + m->numt); + qf = (unsigned short *)(m->data + m->numv + m->numt + m->numn); + + numverts = m->numf/3; + vc_assert(numverts <= MAX_VERTICES); + + temp = allocbuffer(3*numverts*sizeof *temp); + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + mat->numverts = i < m->num_materials-1 ? m->material_index[i+1]-m->material_index[i] : numverts - m->material_index[i]; + // vertex, texture, normal + deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 + + deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 + + deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 + offset += mat->numverts; + mat->texture = model->texture; + } + model->num_materials = m->num_materials; + vc_assert(offset == numverts); + freebuffer(temp); + freebuffer(m); + return (MODEL_T)model; +} + +void unload_wavefront(MODEL_T m) +{ + WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; + int i; + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + if (mat->vbo[VBO_VERTEX]) + destroy_vbo(mat->vbo+VBO_VERTEX); + if (mat->vbo[VBO_TEXTURE]) + destroy_vbo(mat->vbo+VBO_TEXTURE); + if (mat->vbo[VBO_NORMAL]) + destroy_vbo(mat->vbo+VBO_NORMAL); + } +} + +// create a cube model that looks like a wavefront model, +MODEL_T cube_wavefront(void) +{ + static const float qv[] = { + -0.5f, -0.5f, 0.5f, + -0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, 0.5f, + -0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, -0.5f, + -0.5f, 0.5f, -0.5f, + }; + + static const float qn[] = { + 0.0f, -1.0f, -0.0f, + 0.0f, 1.0f, -0.0f, + 0.0f, 0.0f, 1.0f, + 1.0f, 0.0f, -0.0f, + 0.0f, 0.0f, -1.0f, + -1.0f, 0.0f, -0.0f, + }; + + static const float qt[] = { + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + }; + + static const unsigned short qf[] = { + 1,1,1, 2,2,1, 3,3,1, + 3,3,1, 4,4,1, 1,1,1, + 5,4,2, 6,1,2, 7,2,2, + 7,2,2, 8,3,2, 5,4,2, + 1,4,3, 4,1,3, 6,2,3, + 6,2,3, 5,3,3, 1,4,3, + 4,4,4, 3,1,4, 7,2,4, + 7,2,4, 6,3,4, 4,4,4, + 3,4,5, 2,1,5, 8,2,5, + 8,2,5, 7,3,5, 3,4,5, + 2,4,6, 1,1,6, 5,2,6, + 5,2,6, 8,3,6, 2,4,6, + }; + WAVEFRONT_MODEL_T *model = malloc(sizeof *model); + if (model) { + WAVEFRONT_MATERIAL_T *mat = model->material; + float *temp; + const int offset = 0; + memset(model, 0, sizeof *model); + + temp = allocbuffer(3*MAX_VERTICES*sizeof *temp); + mat->numverts = countof(qf)/3; + // vertex, texture, normal + deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 + + deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 + + deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 + + freebuffer(temp); + model->num_materials = 1; + } + return (MODEL_T)model; +} + + diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/models.h b/host_applications/linux/apps/hello_pi/hello_teapot/models.h new file mode 100755 index 0000000..4a6cbd0 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/models.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MODELS_T +#define MODELS_T +typedef struct opqaue_model_s * MODEL_T; + +MODEL_T load_wavefront(const char *modelname, const char *texturename); +MODEL_T cube_wavefront(void); +void unload_wavefront(MODEL_T m); +int draw_wavefront(MODEL_T m, GLuint texture); +#endif diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/teapot.obj.dat b/host_applications/linux/apps/hello_pi/hello_teapot/teapot.obj.dat new file mode 100755 index 0000000..584ab3d Binary files /dev/null and b/host_applications/linux/apps/hello_pi/hello_teapot/teapot.obj.dat differ diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/triangle.c b/host_applications/linux/apps/hello_pi/hello_teapot/triangle.c new file mode 100755 index 0000000..8b9c2dd --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/triangle.c @@ -0,0 +1,468 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, OtherCrashOverride +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// A rotating cube rendered with OpenGL|ES. Three images used as textures on the cube faces. + +#include +#include +#include +#include +#include +#include + +#include "bcm_host.h" + +#include "GLES/gl.h" +#include "EGL/egl.h" +#include "EGL/eglext.h" + +#include "cube_texture_and_coords.h" +#include "models.h" +#include "triangle.h" +#include + + +#define PATH "./" + +#define IMAGE_SIZE_WIDTH 1920 +#define IMAGE_SIZE_HEIGHT 1080 + +#ifndef M_PI + #define M_PI 3.141592654 +#endif + + +typedef struct +{ + uint32_t screen_width; + uint32_t screen_height; +// OpenGL|ES objects + EGLDisplay display; + EGLSurface surface; + EGLContext context; + GLuint tex; +// model rotation vector and direction + GLfloat rot_angle_x_inc; + GLfloat rot_angle_y_inc; + GLfloat rot_angle_z_inc; +// current model rotation angles + GLfloat rot_angle_x; + GLfloat rot_angle_y; + GLfloat rot_angle_z; +// current distance from camera + GLfloat distance; + GLfloat distance_inc; + MODEL_T model; +} CUBE_STATE_T; + +static void init_ogl(CUBE_STATE_T *state); +static void init_model_proj(CUBE_STATE_T *state); +static void reset_model(CUBE_STATE_T *state); +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); +static void redraw_scene(CUBE_STATE_T *state); +static void update_model(CUBE_STATE_T *state); +static void init_textures(CUBE_STATE_T *state); +static void exit_func(void); +static volatile int terminate; +static CUBE_STATE_T _state, *state=&_state; + +static void* eglImage = 0; +static pthread_t thread1; + + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void init_ogl(CUBE_STATE_T *state) +{ + int32_t success = 0; + EGLBoolean result; + EGLint num_config; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_ELEMENT_HANDLE_T dispman_element; + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_SAMPLES, 4, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + EGLConfig config; + + // get an EGL display connection + state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + assert(state->display!=EGL_NO_DISPLAY); + + // initialize the EGL display connection + result = eglInitialize(state->display, NULL, NULL); + assert(EGL_FALSE != result); + + // get an appropriate EGL frame buffer configuration + // this uses a BRCM extension that gets the closest match, rather than standard which returns anything that matches + result = eglSaneChooseConfigBRCM(state->display, attribute_list, &config, 1, &num_config); + assert(EGL_FALSE != result); + + // create an EGL rendering context + state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); + assert(state->context!=EGL_NO_CONTEXT); + + // create an EGL window surface + success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); + assert( success >= 0 ); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = state->screen_width; + dst_rect.height = state->screen_height; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = state->screen_width << 16; + src_rect.height = state->screen_height << 16; + + dispman_display = vc_dispmanx_display_open( 0 /* LCD */); + dispman_update = vc_dispmanx_update_start( 0 ); + + dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, + 0/*layer*/, &dst_rect, 0/*src*/, + &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); + + nativewindow.element = dispman_element; + nativewindow.width = state->screen_width; + nativewindow.height = state->screen_height; + vc_dispmanx_update_submit_sync( dispman_update ); + + state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, NULL ); + assert(state->surface != EGL_NO_SURFACE); + + // connect the context to the surface + result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); + assert(EGL_FALSE != result); + + // Set background color and clear buffers + glClearColor((0.3922f+7*0.5f)/8, (0.1176f+7*0.5f)/8, (0.5882f+7*0.5f)/8, 1.0f); + + // Enable back face culling. + glEnable(GL_CULL_FACE); + + glEnable(GL_DEPTH_TEST); + glClearDepthf(1.0); + glDepthFunc(GL_LEQUAL); + + float noAmbient[] = {1.0f, 1.0f, 1.0f, 1.0f}; + glLightfv(GL_LIGHT0, GL_AMBIENT, noAmbient); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHTING); +} + +/*********************************************************** + * Name: init_model_proj + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the OpenGL|ES model to default values + * + * Returns: void + * + ***********************************************************/ +static void init_model_proj(CUBE_STATE_T *state) +{ + float nearp = 0.1f; + float farp = 500.0f; + float hht; + float hwd; + + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + + glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); + hwd = hht * (float)state->screen_width / (float)state->screen_height; + + glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); + + glEnableClientState( GL_VERTEX_ARRAY ); + + reset_model(state); +} + +/*********************************************************** + * Name: reset_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Resets the Model projection and rotation direction + * + * Returns: void + * + ***********************************************************/ +static void reset_model(CUBE_STATE_T *state) +{ + // reset model position + glMatrixMode(GL_MODELVIEW); + + // reset model rotation + state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; + state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; + state->distance = 1.2f*1.5f; +} + +/*********************************************************** + * Name: update_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Updates model projection to current position/rotation + * + * Returns: void + * + ***********************************************************/ +static void update_model(CUBE_STATE_T *state) +{ + // update position + state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); + state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); + state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); + state->distance = inc_and_clip_distance(state->distance, state->distance_inc); + + glLoadIdentity(); + // move camera back to see the cube + glTranslatef(0.f, 0.f, -state->distance); + + // Rotate model to new position + glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); + glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); + glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); +} + +/*********************************************************** + * Name: inc_and_wrap_angle + * + * Arguments: + * GLfloat angle current angle + * GLfloat angle_inc angle increment + * + * Description: Increments or decrements angle by angle_inc degrees + * Wraps to 0 at 360 deg. + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) +{ + angle += angle_inc; + + if (angle >= 360.0) + angle -= 360.f; + else if (angle <=0) + angle += 360.f; + + return angle; +} + +/*********************************************************** + * Name: inc_and_clip_distance + * + * Arguments: + * GLfloat distance current distance + * GLfloat distance_inc distance increment + * + * Description: Increments or decrements distance by distance_inc units + * Clips to range + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) +{ + distance += distance_inc; + + if (distance >= 10.0f) + distance = 10.f; + else if (distance <= 1.0f) + distance = 1.0f; + + return distance; +} + +/*********************************************************** + * Name: redraw_scene + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Draws the model and calls eglSwapBuffers + * to render to screen + * + * Returns: void + * + ***********************************************************/ +static void redraw_scene(CUBE_STATE_T *state) +{ + // Start with a clear screen + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + draw_wavefront(state->model, state->tex); + + eglSwapBuffers(state->display, state->surface); +} + +/*********************************************************** + * Name: init_textures + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Initialise OGL|ES texture surfaces to use image + * buffers + * + * Returns: void + * + ***********************************************************/ +static void init_textures(CUBE_STATE_T *state) +{ + // the texture containing the video + glGenTextures(1, &state->tex); + + glBindTexture(GL_TEXTURE_2D, state->tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE_WIDTH, IMAGE_SIZE_HEIGHT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + /* Create EGL Image */ + eglImage = eglCreateImageKHR( + state->display, + state->context, + EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer)state->tex, + 0); + + if (eglImage == EGL_NO_IMAGE_KHR) + { + printf("eglCreateImageKHR failed.\n"); + exit(1); + } + + // Start rendering + pthread_create(&thread1, NULL, video_decode_test, eglImage); + + // setup overall texture environment + glTexCoordPointer(2, GL_FLOAT, 0, texCoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_TEXTURE_2D); + + // Bind texture surface to current vertices + glBindTexture(GL_TEXTURE_2D, state->tex); +} +//------------------------------------------------------------------------------ + +static void exit_func(void) +// Function to be passed to atexit(). +{ + if (eglImage != 0) + { + if (!eglDestroyImageKHR(state->display, (EGLImageKHR) eglImage)) + printf("eglDestroyImageKHR failed."); + } + + // clear screen + glClear( GL_COLOR_BUFFER_BIT ); + eglSwapBuffers(state->display, state->surface); + + // Release OpenGL resources + eglMakeCurrent( state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + eglDestroySurface( state->display, state->surface ); + eglDestroyContext( state->display, state->context ); + eglTerminate( state->display ); + + printf("\ncube closed\n"); +} // exit_func() + +//============================================================================== + +int main () +{ + bcm_host_init(); + printf("Note: ensure you have sufficient gpu_mem configured\n"); + + // Clear application state + memset( state, 0, sizeof( *state ) ); + + // Start OGLES + init_ogl(state); + + // Setup the model world + init_model_proj(state); + + // initialise the OGLES texture(s) + init_textures(state); + + //state->model = cube_wavefront(); + state->model = load_wavefront("/opt/vc/src/hello_pi/hello_teapot/teapot.obj.dat", NULL); + + while (!terminate) + { + update_model(state); + redraw_scene(state); + } + exit_func(); + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/triangle.h b/host_applications/linux/apps/hello_pi/hello_teapot/triangle.h new file mode 100755 index 0000000..0f50e93 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/triangle.h @@ -0,0 +1,30 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once + + +void* video_decode_test(void* arg); diff --git a/host_applications/linux/apps/hello_pi/hello_teapot/video.c b/host_applications/linux/apps/hello_pi/hello_teapot/video.c new file mode 100755 index 0000000..4baae8f --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_teapot/video.c @@ -0,0 +1,266 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, OtherCrashOverride +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Video decode demo using OpenMAX IL though the ilcient helper library + +#include +#include +#include + +#include "bcm_host.h" +#include "ilclient.h" + +static OMX_BUFFERHEADERTYPE* eglBuffer = NULL; +static COMPONENT_T* egl_render = NULL; + +static void* eglImage = 0; + +void my_fill_buffer_done(void* data, COMPONENT_T* comp) +{ + if (OMX_FillThisBuffer(ilclient_get_handle(egl_render), eglBuffer) != OMX_ErrorNone) + { + printf("OMX_FillThisBuffer failed in callback\n"); + exit(1); + } +} + + +// Modified function prototype to work with pthreads +void *video_decode_test(void* arg) +{ + const char* filename = "/opt/vc/src/hello_pi/hello_video/test.h264"; + eglImage = arg; + + if (eglImage == 0) + { + printf("eglImage is null.\n"); + exit(1); + } + + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; + COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *clock = NULL; + COMPONENT_T *list[5]; + TUNNEL_T tunnel[4]; + ILCLIENT_T *client; + FILE *in; + int status = 0; + unsigned int data_len = 0; + + memset(list, 0, sizeof(list)); + memset(tunnel, 0, sizeof(tunnel)); + + if((in = fopen(filename, "rb")) == NULL) + return (void *)-2; + + if((client = ilclient_init()) == NULL) + { + fclose(in); + return (void *)-3; + } + + if(OMX_Init() != OMX_ErrorNone) + { + ilclient_destroy(client); + fclose(in); + return (void *)-4; + } + + // callback + ilclient_set_fill_buffer_done_callback(client, my_fill_buffer_done, 0); + + // create video_decode + if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) + status = -14; + list[0] = video_decode; + + // create egl_render + if(status == 0 && ilclient_create_component(client, &egl_render, "egl_render", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_OUTPUT_BUFFERS) != 0) + status = -14; + list[1] = egl_render; + + // create clock + if(status == 0 && ilclient_create_component(client, &clock, "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[2] = clock; + + memset(&cstate, 0, sizeof(cstate)); + cstate.nSize = sizeof(cstate); + cstate.nVersion.nVersion = OMX_VERSION; + cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; + cstate.nWaitMask = 1; + if(clock != NULL && OMX_SetParameter(ILC_GET_HANDLE(clock), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) + status = -13; + + // create video_scheduler + if(status == 0 && ilclient_create_component(client, &video_scheduler, "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[3] = video_scheduler; + + set_tunnel(tunnel, video_decode, 131, video_scheduler, 10); + set_tunnel(tunnel+1, video_scheduler, 11, egl_render, 220); + set_tunnel(tunnel+2, clock, 80, video_scheduler, 12); + + // setup clock tunnel first + if(status == 0 && ilclient_setup_tunnel(tunnel+2, 0, 0) != 0) + status = -15; + else + ilclient_change_component_state(clock, OMX_StateExecuting); + + if(status == 0) + ilclient_change_component_state(video_decode, OMX_StateIdle); + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 130; + format.eCompressionFormat = OMX_VIDEO_CodingAVC; + + if(status == 0 && + OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone && + ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) + { + OMX_BUFFERHEADERTYPE *buf; + int port_settings_changed = 0; + int first_packet = 1; + + ilclient_change_component_state(video_decode, OMX_StateExecuting); + + while((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL) + { + // feed data and wait until we get port settings changed + unsigned char *dest = buf->pBuffer; + + // loop if at end + if (feof(in)) + rewind(in); + + data_len += fread(dest, 1, buf->nAllocLen-data_len, in); + + if(port_settings_changed == 0 && + ((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) || + (data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, + ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) + { + port_settings_changed = 1; + + if(ilclient_setup_tunnel(tunnel, 0, 0) != 0) + { + status = -7; + break; + } + + ilclient_change_component_state(video_scheduler, OMX_StateExecuting); + + // now setup tunnel to egl_render + if(ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0) + { + status = -12; + break; + } + + // Set egl_render to idle + ilclient_change_component_state(egl_render, OMX_StateIdle); + + // Enable the output port and tell egl_render to use the texture as a buffer + //ilclient_enable_port(egl_render, 221); THIS BLOCKS SO CAN'T BE USED + if (OMX_SendCommand(ILC_GET_HANDLE(egl_render), OMX_CommandPortEnable, 221, NULL) != OMX_ErrorNone) + { + printf("OMX_CommandPortEnable failed.\n"); + exit(1); + } + + if (OMX_UseEGLImage(ILC_GET_HANDLE(egl_render), &eglBuffer, 221, NULL, eglImage) != OMX_ErrorNone) + { + printf("OMX_UseEGLImage failed.\n"); + exit(1); + } + + // Set egl_render to executing + ilclient_change_component_state(egl_render, OMX_StateExecuting); + + + // Request egl_render to write data to the texture buffer + if(OMX_FillThisBuffer(ILC_GET_HANDLE(egl_render), eglBuffer) != OMX_ErrorNone) + { + printf("OMX_FillThisBuffer failed.\n"); + exit(1); + } + } + if(!data_len) + break; + + buf->nFilledLen = data_len; + data_len = 0; + + buf->nOffset = 0; + if(first_packet) + { + buf->nFlags = OMX_BUFFERFLAG_STARTTIME; + first_packet = 0; + } + else + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + { + status = -6; + break; + } + } + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + status = -20; + + // need to flush the renderer to allow video_decode to disable its input port + ilclient_flush_tunnels(tunnel, 0); + + ilclient_disable_port_buffers(video_decode, 130, NULL, NULL, NULL); + } + + fclose(in); + + ilclient_disable_tunnel(tunnel); + ilclient_disable_tunnel(tunnel+1); + ilclient_disable_tunnel(tunnel+2); + ilclient_teardown_tunnels(tunnel); + + ilclient_state_transition(list, OMX_StateIdle); + ilclient_state_transition(list, OMX_StateLoaded); + + ilclient_cleanup_components(list); + + OMX_Deinit(); + + ilclient_destroy(client); + return (void *)status; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_tiger/CMakeLists.txt new file mode 100755 index 0000000..b253f3f --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/CMakeLists.txt @@ -0,0 +1,9 @@ +set(EXEC hello_tiger.bin) +set(SRCS main.c tiger.c) +add_definitions(-D__RASPBERRYPI__) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/Makefile b/host_applications/linux/apps/hello_pi/hello_tiger/Makefile new file mode 100755 index 0000000..f95e244 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/Makefile @@ -0,0 +1,8 @@ +OBJS=main.o tiger.o +BIN=hello_tiger.bin + +#LDFLAGS+= +CFLAGS+=-D__RASPBERRYPI__ + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/license.txt b/host_applications/linux/apps/hello_pi/hello_tiger/license.txt new file mode 100755 index 0000000..07599fc --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/license.txt @@ -0,0 +1,53 @@ +OpenVG 1.1 Reference Implementation +----------------------------------- + +Copyright (c) 2007 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and /or associated documentation files +(the "Materials "), to deal in the Materials without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Materials, +and to permit persons to whom the Materials are 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 Materials. + +THE MATERIALS ARE 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 MATERIALS OR +THE USE OR OTHER DEALINGS IN THE MATERIALS. + + +Path data for the Tiger sample program has been extracted from Ghostscript's +tiger.eps example file distributed under GNU General Public License. + +Ghostscript's License document: +" The files in the src, lib, toolbin, examples, doc and man + directories (folders) and any subdirectories (sub-folders) + thereof are part of GPL Ghostscript. + + The files in the Resource directory and any subdirectories thereof + are also part of GPL Ghostscript, with the explicit exception of + the files in the CMap subdirectory. The CMap files are copyright + Adobe Systems Incorporated and covered by a separate license + which permits only verbatim distribution. + + GPL Ghostscript is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + GPL Ghostscript is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program so you can know your rights and responsibilities. + It should be in a file named doc/COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place Suite 330, Boston, MA + 02111-1307, USA." diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/main.c b/host_applications/linux/apps/hello_pi/hello_tiger/main.c new file mode 100755 index 0000000..a15dda6 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/main.c @@ -0,0 +1,533 @@ +/*------------------------------------------------------------------------ + * + * OpenVG 1.0.1 Reference Implementation sample code + * ------------------------------------------------- + * + * Copyright (c) 2007 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and /or associated documentation files + * (the "Materials "), to deal in the Materials without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Materials, + * and to permit persons to whom the Materials are 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 Materials. + * + * THE MATERIALS ARE 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 MATERIALS OR + * THE USE OR OTHER DEALINGS IN THE MATERIALS. + * + *//** + * \file + * \brief Tiger sample application. Resizing the application window + * rerenders the tiger in the new resolution. Pressing 1,2,3 + * or 4 sets pixel zoom factor, mouse moves inside the zoomed + * image (mouse move works on OpenGL >= 1.2). + * \note + *//*-------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#define UNREF(X) ((void)(X)) + +#ifdef HG_FLAT_INCLUDES +# include "openvg.h" +# include "vgu.h" +# include "egl.h" +#else +# include "VG/openvg.h" +# include "VG/vgu.h" +# include "EGL/egl.h" +#endif + +#include "tiger.h" + +/*--------------------------------------------------------------*/ + +#ifdef __RASPBERRYPI__ +static float rotateN = 0.0f; +#endif +const float aspectRatio = 612.0f / 792.0f; +int renderWidth = 0; +int renderHeight = 0; +EGLDisplay egldisplay; +EGLConfig eglconfig; +EGLSurface eglsurface; +EGLContext eglcontext; + +/*--------------------------------------------------------------*/ + +typedef struct +{ + VGFillRule m_fillRule; + VGPaintMode m_paintMode; + VGCapStyle m_capStyle; + VGJoinStyle m_joinStyle; + float m_miterLimit; + float m_strokeWidth; + VGPaint m_fillPaint; + VGPaint m_strokePaint; + VGPath m_path; +} PathData; + +typedef struct +{ + PathData* m_paths; + int m_numPaths; +} PS; + +PS* PS_construct(const char* commands, int commandCount, const float* points, int pointCount) +{ + PS* ps = (PS*)malloc(sizeof(PS)); + int p = 0; + int c = 0; + int i = 0; + int paths = 0; + int maxElements = 0; + unsigned char* cmd; + UNREF(pointCount); + + while(c < commandCount) + { + int elements, e; + c += 4; + p += 8; + elements = (int)points[p++]; + assert(elements > 0); + if(elements > maxElements) + maxElements = elements; + for(e=0;em_numPaths = paths; + ps->m_paths = (PathData*)malloc(paths * sizeof(PathData)); + cmd = (unsigned char*)malloc(maxElements); + + i = 0; + p = 0; + c = 0; + while(c < commandCount) + { + int elements, startp, e; + float color[4]; + + //fill type + int paintMode = 0; + ps->m_paths[i].m_fillRule = VG_NON_ZERO; + switch( commands[c] ) + { + case 'N': + break; + case 'F': + ps->m_paths[i].m_fillRule = VG_NON_ZERO; + paintMode |= VG_FILL_PATH; + break; + case 'E': + ps->m_paths[i].m_fillRule = VG_EVEN_ODD; + paintMode |= VG_FILL_PATH; + break; + default: + assert(0); //unknown command + } + c++; + + //stroke + switch( commands[c] ) + { + case 'N': + break; + case 'S': + paintMode |= VG_STROKE_PATH; + break; + default: + assert(0); //unknown command + } + ps->m_paths[i].m_paintMode = (VGPaintMode)paintMode; + c++; + + //line cap + switch( commands[c] ) + { + case 'B': + ps->m_paths[i].m_capStyle = VG_CAP_BUTT; + break; + case 'R': + ps->m_paths[i].m_capStyle = VG_CAP_ROUND; + break; + case 'S': + ps->m_paths[i].m_capStyle = VG_CAP_SQUARE; + break; + default: + assert(0); //unknown command + } + c++; + + //line join + switch( commands[c] ) + { + case 'M': + ps->m_paths[i].m_joinStyle = VG_JOIN_MITER; + break; + case 'R': + ps->m_paths[i].m_joinStyle = VG_JOIN_ROUND; + break; + case 'B': + ps->m_paths[i].m_joinStyle = VG_JOIN_BEVEL; + break; + default: + assert(0); //unknown command + } + c++; + + //the rest of stroke attributes + ps->m_paths[i].m_miterLimit = points[p++]; + ps->m_paths[i].m_strokeWidth = points[p++]; + + //paints + color[0] = points[p++]; + color[1] = points[p++]; + color[2] = points[p++]; + color[3] = 1.0f; + ps->m_paths[i].m_strokePaint = vgCreatePaint(); + vgSetParameteri(ps->m_paths[i].m_strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetParameterfv(ps->m_paths[i].m_strokePaint, VG_PAINT_COLOR, 4, color); + + color[0] = points[p++]; + color[1] = points[p++]; + color[2] = points[p++]; + color[3] = 1.0f; + ps->m_paths[i].m_fillPaint = vgCreatePaint(); + vgSetParameteri(ps->m_paths[i].m_fillPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetParameterfv(ps->m_paths[i].m_fillPaint, VG_PAINT_COLOR, 4, color); + + //read number of elements + + elements = (int)points[p++]; + assert(elements > 0); + startp = p; + for(e=0;em_paths[i].m_path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, (unsigned int)VG_PATH_CAPABILITY_ALL); + vgAppendPathData(ps->m_paths[i].m_path, elements, cmd, points + startp); + i++; + } + free(cmd); + return ps; +} + +void PS_destruct(PS* ps) +{ + int i; + assert(ps); + for(i=0;im_numPaths;i++) + { + vgDestroyPaint(ps->m_paths[i].m_fillPaint); + vgDestroyPaint(ps->m_paths[i].m_strokePaint); + vgDestroyPath(ps->m_paths[i].m_path); + } + free(ps->m_paths); + free(ps); +} + +void PS_render(PS* ps) +{ + int i; + assert(ps); + vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER); + + for(i=0;im_numPaths;i++) + { + vgSeti(VG_FILL_RULE, ps->m_paths[i].m_fillRule); + vgSetPaint(ps->m_paths[i].m_fillPaint, VG_FILL_PATH); + + if(ps->m_paths[i].m_paintMode & VG_STROKE_PATH) + { + vgSetf(VG_STROKE_LINE_WIDTH, ps->m_paths[i].m_strokeWidth); + vgSeti(VG_STROKE_CAP_STYLE, ps->m_paths[i].m_capStyle); + vgSeti(VG_STROKE_JOIN_STYLE, ps->m_paths[i].m_joinStyle); + vgSetf(VG_STROKE_MITER_LIMIT, ps->m_paths[i].m_miterLimit); + vgSetPaint(ps->m_paths[i].m_strokePaint, VG_STROKE_PATH); + } + + vgDrawPath(ps->m_paths[i].m_path, ps->m_paths[i].m_paintMode); + } + assert(vgGetError() == VG_NO_ERROR); +} + +PS* tiger = NULL; + +/*--------------------------------------------------------------*/ + +void render(int w, int h) +{ +#ifndef __RASPBERRYPI__ + if(renderWidth != w || renderHeight != h) +#endif + { + float clearColor[4] = {1,1,1,1}; + float scale = w / (tigerMaxX - tigerMinX); + + eglSwapBuffers(egldisplay, eglsurface); //force EGL to recognize resize + + vgSetfv(VG_CLEAR_COLOR, 4, clearColor); + vgClear(0, 0, w, h); + + vgLoadIdentity(); +#ifdef __RASPBERRYPI__ + vgTranslate(w * 0.5f, h * 0.5f); + vgRotate(rotateN); + vgTranslate(-w * 0.5f, -h * 0.5f); +#endif + vgScale(scale, scale); + vgTranslate(-tigerMinX, -tigerMinY + 0.5f * (h / scale - (tigerMaxY - tigerMinY))); + + PS_render(tiger); + assert(vgGetError() == VG_NO_ERROR); + + renderWidth = w; + renderHeight = h; + } +#ifndef __RASPBERRYPI__ + eglSwapBuffers(egldisplay, eglsurface); + assert(eglGetError() == EGL_SUCCESS); +#endif +} + +/*--------------------------------------------------------------*/ + +void init(NativeWindowType window) +{ + static const EGLint s_configAttribs[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_LUMINANCE_SIZE, EGL_DONT_CARE, //EGL_DONT_CARE + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_SAMPLES, 1, + EGL_NONE + }; + EGLint numconfigs; + + egldisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(egldisplay, NULL, NULL); + assert(eglGetError() == EGL_SUCCESS); + eglBindAPI(EGL_OPENVG_API); + + eglChooseConfig(egldisplay, s_configAttribs, &eglconfig, 1, &numconfigs); + assert(eglGetError() == EGL_SUCCESS); + assert(numconfigs == 1); + + eglsurface = eglCreateWindowSurface(egldisplay, eglconfig, window, NULL); + assert(eglGetError() == EGL_SUCCESS); + eglcontext = eglCreateContext(egldisplay, eglconfig, NULL, NULL); + assert(eglGetError() == EGL_SUCCESS); + eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglcontext); + assert(eglGetError() == EGL_SUCCESS); + + tiger = PS_construct(tigerCommands, tigerCommandCount, tigerPoints, tigerPointCount); +} + +/*--------------------------------------------------------------*/ + +void deinit(void) +{ + PS_destruct(tiger); + eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + assert(eglGetError() == EGL_SUCCESS); + eglTerminate(egldisplay); + assert(eglGetError() == EGL_SUCCESS); + eglReleaseThread(); +} + +/*--------------------------------------------------------------*/ + +#ifdef WIN32 +#pragma warning(disable:4115) /* named type definition in parentheses (this comes from a visual studio include file) */ +#include + +static LONG WINAPI windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_CLOSE: + case WM_DESTROY: + PostQuitMessage(0); + return 0; + case WM_PAINT: + { + RECT rect; + InvalidateRect(hWnd, NULL, 0); + GetClientRect(hWnd, &rect); + render(rect.right - rect.left, rect.bottom - rect.top); + return 0; + } + default: + break; + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +/*--------------------------------------------------------------*/ + +int main(void) +{ + HWND window; + { + WNDCLASS wndclass; + wndclass.style = 0; + wndclass.lpfnWndProc = windowProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = (HINSTANCE)GetModuleHandle(NULL); + wndclass.hIcon = LoadIcon(wndclass.hInstance, MAKEINTRESOURCE(101)); + wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclass.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = "MainWndClass"; + if (!wndclass.hIcon) + wndclass.hIcon = LoadIcon(NULL, IDI_EXCLAMATION); + RegisterClass(&wndclass); + } + + window = CreateWindow( + "MainWndClass", + "OpenVG Tiger sample (rendering, please wait)", + WS_OVERLAPPEDWINDOW, + 200, 200, 400, (int)(400.0f / aspectRatio), + NULL, + NULL, + (HINSTANCE)GetModuleHandle(NULL), + NULL); + if (!window) + return -1; + + init((NativeWindowType)window); + + { + MSG msg; + ShowWindow(window, SW_SHOW); + while (GetMessage(&msg, NULL, 0, 0)) + { + DispatchMessage(&msg); + if (msg.message == WM_QUIT) + break; + } + } + + deinit(); + + DestroyWindow(window); + return 0; +} + +/*--------------------------------------------------------------*/ + +#elif defined __APPLE__ + +/*--------------------------------------------------------------*/ + +#include + +//TODO + +#elif defined __RASPBERRYPI__ +#include "bcm_host.h" +int main(void) +{ + uint32_t width, height; + bcm_host_init(); + int s; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_ELEMENT_HANDLE_T dispman_element; + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + s = graphics_get_display_size(0 /* LCD */, &width, &height); + assert( s >= 0 ); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = width; + dst_rect.height = height; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = width << 16; + src_rect.height = height << 16; + + dispman_display = vc_dispmanx_display_open( 0 /* LCD */); + dispman_update = vc_dispmanx_update_start( 0 ); + + dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, + 1/*layer*/, &dst_rect, 0/*src*/, + &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); + + nativewindow.element = dispman_element; + nativewindow.width = width; + nativewindow.height = height; + vc_dispmanx_update_submit_sync( dispman_update ); + + init(&nativewindow); + + while (1) { + render(width, height); + rotateN += 1.0f; + } + deinit(); + + return 0; +} +#endif + diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/readme.txt b/host_applications/linux/apps/hello_pi/hello_tiger/readme.txt new file mode 100755 index 0000000..6845e24 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/readme.txt @@ -0,0 +1,263 @@ +OpenVG 1.1 Reference Implementation +----------------------------------- + +Copyright (c) 2007 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and /or associated documentation files +(the "Materials "), to deal in the Materials without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Materials, +and to permit persons to whom the Materials are 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 Materials. + +THE MATERIALS ARE 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 MATERIALS OR +THE USE OR OTHER DEALINGS IN THE MATERIALS. + + +Version +------- +Official RI for OpenVG 1.1 +Released: May 13, 2008 + + +Release Notes +------------- + +This release is based on OpenVG 1.1 and EGL 1.3 specifications. +This release is Windows-only, although the source code +compiles at least on Mac OS X 10.5 and Cygwin. Project files are +provided for MSVC 6. + +This archive contains sources for OpenVG RI, VGU and EGL. There's +also a precompiled libOpenVG.dll that contains OpenVG and EGL implementations. + + +Package Structure +----------------- + +bin + win32 + libOpenVG.dll OpenVG Windows .dll + tiger.exe Windows executable of the Tiger sample +lib + libOpenVG.lib MSVC 6 dll import library +ri + openvg_ri.dsp MSVC 6 project file for libOpenVG.dll + src .cpp and .h -files of the reference implementation + win32 Windows backend for EGL + macosx Mac OS X backend for EGL + null null backend for EGL + include Public OpenVG and EGL headers + EGL + egl.h + VG + openvg.h + vgu.h +samples + samples.dsw MSVC 6 workspace file for tiger sample and libOpenVG.dll + samples.dsp MSVC 6 project file for tiger.exe + tiger + main.c + tiger.c + tiger.h +readme.txt +license.txt + + +Samples +------- + +Tiger + +The release contains a sample application that renders an image of a +tiger. Note that the sample doesn't start immediately, since it takes +a few seconds to render the image. Resizing the window rerenders the +image in the new resolution. + + +Known Issues +------------ + +-EGL functionality is incomplete (some functions lack proper error checking, some + attribs may not be processed, etc.) +-When opening samples.dsw, MSVC may complain about missing Perforce connection. Just + ignore that. + + +Changes +------- + +Nov 25, 2008 +-Clamp color transform scale to [-127, 127] and bias to [-1, 1] + +May 13, 2008 +- Changed 8 sample MSAA configs into 32 sample configs +- Changed max gaussian std deviation to 16 (was 128) +- VG_DRAW_IMAGE_MULTIPLY converts luminance to RGB if either paint or image color is RGB +- Fixes A40102 by storing input floats as is + +February 12, 2008 +- fixed arc transformation. +- fixed point along path corner cases. +- partially fixed A40102 by not filtering invalid float input. + +December 12, 2007 +- fixed an overflow bug in vgFillMaskLayer error handling. +- increased accuracy for Gaussian blur. The new code avoids an infinite loop with a very small std dev. +- fixed a bug in Font::find that caused deleted fonts to be returned. + +November 20, 2007 +- reimplemented 1,2 & 4 bits per pixel images +- fixed vgGetParameter for paint +- fixed https://cvs.khronos.org/bugzilla/show_bug.cgi?id=1095 RI handling of child images with shared storage + -vgGetParent: return closest valid ancestor, or image itself if no ancestor was found. +- EGL refactoring & clean up +- divided OS native parts of EGL into separate files that should be included in that platform's build +- added a generic OS backend for EGL (not thread safe, no window rendering) +- fixed https://cvs.khronos.org/bugzilla/show_bug.cgi?id=1943 RI does not handle channel mask correctly for lL/sL/BW1 +- removed EGL_IMAGE_IN_USE_ERROR from vgDrawGlyph(s) +- added configs without an alpha mask to facilitate CTS reference image creation +- implemented more accurate stroking by rendering each stroke part into a coverage buffer and compositing from there + -fixes https://cvs.khronos.org/bugzilla/show_bug.cgi?id=2221 RI errors at end of curved strokes. +- bugfix: joins used path midpoints, interpolateStroke and caps didn't => seams (visible in some G60101 cases) +- vgCreateMaskLayer returns VG_INVALID_HANDLE if the current surface doesn't have a mask layer +- vgRenderToMask bugfix: temp buffer is now cleared between fill and stroke +- bugfix: vgCreateImage returns an error if allowedQuality is zero +- bugfix: vgSetPaint returns an error if paintModes is zero +- bugfix: vgCreateFont doesn't return an error if capacityHint is zero +- bugfix: writeFilteredPixel writes also into luminance formats + +October 12, 2007 +-Upgrade to OpenVG 1.1, including + -Implemented MSAA, added 4 and 8 sample EGLConfigs + -Implemented VG_A_4 and VG_A_1 image formats and EGLConfigs + -Implemented Glyph API + -Implemented new masking functions + -Implemented color transform +-Implemented native EGL backends for Windows & Mac OS X => Fix for bugzilla 1376 RI uses non-standard EGL implementation + *RI now works with CTS generation's native_w32.c (native_ri.c can be removed). + *dependency on GLUT has been removed. GLUT code is still included and can be compiled in instead of native by defining RI_USE_GLUT. +-16 bit EGLConfigs now expose 4 mask bits, 8 bit configs 8, 4 bit configs 4, and 1 bit configs 1. MSAA configs expose one bit per sample. +-EGL now works with any display, not just EGL_DEFAULT_DISPLAY +-Simplification: removed code to handle less than 8 bits per pixel. Smaller bit depths always allocate 8 bits per pixel. +-Changed rasterizer data types to RScalar and RVector2 so that it's possible to alter RIfloat precision without affecting rasterization. +-Accuracy: increased circularLerp precision +-Bugfix: matrix inversion now checks if the input matrix is affine and forces the inverted matrix to be affine as well +-Bugfix: fixed eglCopyBuffers (copied from dst to dst) +-Bugfix: fixed eglCreatePixmapSurface (allowed only VG_sRGBA_8888, didn't give an error if config had more than one sample per pixel) +-Bugfix: bugzilla 2465: RI asserts when setting maximum amount of gradient stops + +February 27, 2007 +-changed to MIT open source license. +-bugfix, bugzilla 820: RGB and luminance are now treated as different color spaces. +-bugfix, bugzilla 1094/1095: vgGetParent now returns the input image in case its parent is already destroyed. + +December 1, 2006 +-bugfix, bugzilla 649: allowed image quality is now taken into account when deciding resampling filter. +-bugfix, bugzilla 650, bad stroking accuracy reported by TK Chan and Mika Tuomi: curve tessellation is now increased from 64 to 256. RI_MAX_EDGES has been increased from 100000 to 262144 to facilitate the increased number of edges. +-bugfix, reported by Chris Wynn, affects I30206: degenerate gradients in repeat mode now render the first stop color instead of the last one. +-changed float to RIfloat, added an option to compile RIfloat into a class to test reduced precision float ops + +September 6, 2006 +-bugfix, bugzilla 591: CLOSE_PATH followed by a MOVE_TO doesn't produce an extra end cap anymore +-abs values of arc axis lengths are taken only just before rendering +-undefined bits of bitfields are now ignored in the API + +September 1, 2006 +-changed colorToInt to use mathematical round-to-nearest as recommended by new language in section 3.4.4. +-implemented VG_PAINT_COLOR_RAMP_PREMULTIPLIED +-implemented VG_STROKE_DASH_PHASE_RESET +-implemented new language for filter channelMasks (section 11.2) +-tangents returned by vgPointAlongPath are now normalized +-implemented VG_MAX_GAUSSIAN_STD_DEVIATION, rewrote Gaussian blur code +-vgGetString: if no context, return NULL. VG_VERSION returns the spec version (1.0). +-bugfix, bugzilla 542: vgSeparableConvolve now convolves the edge color with the horizontal kernel and uses that as the edge color for the vertical pass +-ellipse rh and rv are replaced by their absolute values whenever read for processing, the absolute values are not written into the path data + +August 18, 2006 +-bugfix, M30301: the arguments for vguComputeWarpQuadToQuad were the wrong way around, destination should come before source. +-bugfix, M10102: check for degeneracy in vguComputeWarpSquareToQuad is done before the affinity check so that degenerate affine matrices also produce the bad warp error +-bugfix, bugzilla 491: Chris Wynn's vgComputeWarpSquareToQuad -case. There was a wrong mapping between vertices, (1,1) was mapped to (dx2,dy2) +-bugfix, bugzilla 519: vguPolygon wrong error check. vguPolygon didn't have an error check for the count argument +-bugfix, bugzilla 518: vgGetParameterfv/iv wrong errors. vgGetParametrtfv/iv error checking was for count < 0 instead of count <= 0. +-bugfix, bugzilla 517: wrong cap flag checked in vgPathTransformedBounds +-bugfix, bugzilla 494: egl.h has wrong values for OPENVG_BIT and OPENGL_ES_BIT. Copied the enumerations from the 1.3 egl.h on the Khronos site (OpenKode/egl/egl.h) +-bugfix, bugzilla 492: gradient filter window was biased +-bugfix: when appending paths, there was a loop over coordinates to replace arc axis lengths by their absolute values. However, if the path wasn't empty, the loop accessed wrong coordinates. Fixes: Qingping Zhang's cases 2&3. +-bugfix: image filter write mask was ignored when writing to VG_A_8 images. Fixes: Qingping Zhang's case 13. +-bugfix: if image filter processing format is premultiplied, color channels are clamped to alpha before conversion to destination format +-bugfix: in eglReleaseThread the EGL instance was freed when its reference count reached zero, but the pointer wasn't made NULL, causing the use of uninitialized instance. +-bugfix: vgClearImage didn't clamp the clear color to [0,1] range +-bugfix: a zero-length dash at a path vertex produces a join +-bugfix: vgSetParameter now checks paramType for all object types +-bugfix: convolution filters incorrectly restricted the area read from the source image to the intersection of source and destination image sizes +-bugfix: EGL surface creation now defaults correctly to EGL_COLOR_SPACE_sRGB +-antialiasing is done in the linear color space as the spec recommends. +-image filters clamp the result to [0,1] range. +-Color::pack and Color::convert assert that their input is in [0,1] range +-in case a projective transform is used, VGImageMode is always VG_DRAW_IMAGE_NORMAL +-the default value for VG_FILTER_FORMAT_LINEAR is now VG_FALSE +-added Matrix::isAffine for easy affinity check +-Color::clamp clamps color channels to alpha for premultiplied colors +-VG_BLEND_LIGHTEN: color channels cannot exceed alpha anymore +-RI now supports flexible pixel formats. Any bit depth for RGBA is now supported. +-eglGetProcAddress is now properly implemented, it returns a function pointer for eglSetConfigPreferenceHG extension +-eglQueryString now returns "eglSetConfigPreferenceHG" for EGL_EXTENSIONS +-location of egl.h in RI. use EGL/egl.h, VG/openvg.h, VG/vgu.h +-OpenVG 1.0.1 spec changes + +use the latest openvg.h + +2.8: AA happens in linear space + +3.4: alpha channel depth of zero results in alpha=1 when read + +4.1: return VG_NO_CONTEXT_ERROR from vgGetError in case no context is current + +5.1: VG_SCREEN_LAYOUT (default = screen layout of the display) + +5.2, 5.3: vgSet, vgGet, vgSetParameter, vgGetParameter: handling of invalid values of count + +5.2.1: new default for VG_FILTER_FORMAT_LINEAR is VG_FALSE + +8.5.3: get rid of VG_PATH_DATATYPE_INVALID and VG_IMAGE_FORMAT_INVALID enums + +10.2: get rid of old extension image formats, add the official ones + +10.5: when reading/writing pixels, clamp color channels to [0, alpha] + +10.8: when a projective transform is used, always use VG_DRAW_IMAGE_NORMAL mode + +10.8: VG_DRAW_IMAGE_MULTIPLY: if color spaces of paint and image don't match, no conversion takes place, result is in image color space + +12.4: clamp the result of additive blend to [0,1] + +October 20, 2005 +-Gradients are filtered to avoid aliasing +-Subpaths that ended with a close path segment were capped and joined incorrectly. Fixed. +-Alpha mask was allocated per context, not per EGL surface. Fixed. + +August 22, 2005 +-Updated to spec amendment +-Fixed bugs +-Implemented eglChooseConfig and eglReleaseThread + +July 22, 2005 +-Updated to 18th July 2005 version of the OpenVG 1.0 spec. +-Updated to 20th July 2005 version of the EGL 1.2 spec. +-Fixed bugs. +-openvg.h, vgu.h and egl.h are now contained in include/vg directory. + +May 4, 2005 +-Updated to April 26th 2005 version of the OpenVG 1.0 spec. +-Can share images, paths, and paint between contexts. +-Fixed path tangent computation. +-Implemented image filters. +-Fixed bugs. +-Changed directory structure a bit. + +March 29, 2005 +-Updated to March 28th 2005 version of the OpenVG 1.0 spec. +-Changed rasterizer to use 32 samples per pixel in the high quality + mode (renders faster at the expense of some aliasing). +-EGL allocates sRGB rendering surfaces. +-Includes GLUT dll against which tiger.exe was linked. + +March 24, 2005 +-Initial release. diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/tiger.c b/host_applications/linux/apps/hello_pi/hello_tiger/tiger.c new file mode 100755 index 0000000..ae2eff9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/tiger.c @@ -0,0 +1,1952 @@ +/*------------------------------------------------------------------------ + * + * OpenVG 1.0.1 Reference Implementation sample code + * ------------------------------------------------- + * + * Copyright (c) 2007 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and /or associated documentation files + * (the "Materials "), to deal in the Materials without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Materials, + * and to permit persons to whom the Materials are 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 Materials. + * + * THE MATERIALS ARE 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 MATERIALS OR + * THE USE OR OTHER DEALINGS IN THE MATERIALS. + * + *//** + * \file + * \brief Path and paint data for Tiger image. + * \note + *//*-------------------------------------------------------------------*/ + +const int tigerCommandCount = 4151; +const char tigerCommands[4151] = { +'F', 'N', 'B', 'M', 'M', 'L', 'L', 'L', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', +'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', +'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', +'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', +'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', +'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', +'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', +'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', +'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', +'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', +'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'N', 'S', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', +'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'L', 'L', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', +'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', +'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', +'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', +'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'L', 'C', 'C', +'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', +'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', +'L', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'L', 'C', 'C', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', +'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'N', +'S', 'B', 'M', 'M', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', +'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', +'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', +'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', +'C', 'C', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'N', 'S', +'B', 'M', 'M', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', +'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', +'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', +'C', 'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', +'E', 'N', 'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', +'S', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'B', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'L', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', +'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', +'C', 'L', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'C', 'C', +'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'C', 'C', 'C', +'C', 'L', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'C', 'C', 'C', 'C', +'L', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', +'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', +'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'L', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'L', 'L', 'C', 'E', 'F', 'N', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', +'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'L', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'L', 'L', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'L', 'C', 'C', 'C', 'C', 'C', +'C', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'L', 'L', 'L', 'N', 'S', 'R', 'M', 'M', 'C', 'C', +'N', 'S', 'R', 'M', 'M', 'C', 'C', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'N', 'S', 'R', 'M', 'M', +'C', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'E', 'F', 'N', +'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'L', +'L', 'C', 'C', 'C', 'C', 'L', 'L', 'C', 'C', 'C', 'C', 'C', 'C', 'L', 'C', 'F', 'N', 'B', 'M', 'M', +'C', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'L', 'C', 'C', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', +'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', +'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', +'L', 'L', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', +'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', +'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', +'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', +'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', +'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', +'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', +'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', +'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', +'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', +'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', +'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', +'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', +'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', +'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', +'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', +'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', +'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', +'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', +'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', +'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', +'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'N', 'S', 'R', 'M', 'M', 'C', 'C', 'C', 'E', +'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', +'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', +'F', 'N', 'B', 'M', 'M', 'C', 'C', 'L', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', +'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', +'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'C', 'C', +'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', +'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', +'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', +'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', +'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', +'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', 'N', 'R', 'M', 'M', 'C', 'C', 'C', 'E', 'F', +'N', 'R', 'M', 'M', 'L', 'C', 'L', 'N', 'S', 'R', 'M', 'M', 'L', 'N', 'S', 'R', 'M', 'M', 'C', 'N', +'S', 'R', 'M', 'M', 'C', 'N', 'S', 'R', 'M', 'M', 'C'}; + +const float tigerMinX = 0.0f; +const float tigerMaxX = 612.0f; +const float tigerMinY = 0.0f; +const float tigerMaxY = 792.0f; + +const int tigerPointCount = 17005; +const float tigerPoints[17005] = { +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 0, +792, 0, 0, 612, 0, 612, 792, 10, 0, 1, +1, 1, 1, 1, 1, 5, 85.25f, 487.75f, 85.25f, 487.75f, +85.5742f, 485.199f, 84.25f, 484.746f, 83.7617f, 485.242f, 65.6641f, 538.125f, 43.2461f, 535.746f, +43.2422f, 535.746f, 62.6445f, 543.746f, 85.25f, 487.75f, 10, 0.1892f, 0, 0, +0, 0, 0, 0, 5, 85.25f, 487.75f, 85.25f, 487.75f, 85.5742f, +485.199f, 84.25f, 484.746f, 83.7617f, 485.242f, 65.6641f, 538.125f, 43.2461f, 535.746f, 43.2422f, +535.746f, 62.6445f, 543.746f, 85.25f, 487.75f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 89.2461f, 490.75f, 89.2461f, 490.75f, 88.7422f, 488.613f, +88.2461f, 488.746f, 87.0508f, 489.27f, 88.0234f, 545.156f, 66.2461f, 550.746f, 66.2461f, 550.742f, +87.0977f, 551.469f, 89.2461f, 490.75f, 10, 0.1892f, 0, 0, 0, 0, +0, 0, 5, 89.2461f, 490.75f, 89.2461f, 490.75f, 88.7422f, 488.613f, 88.2461f, +488.746f, 87.0508f, 489.27f, 88.0234f, 545.156f, 66.2461f, 550.746f, 66.2461f, 550.742f, 87.0977f, +551.469f, 89.2461f, 490.75f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 119.25f, 443.75f, 119.25f, 443.75f, 121.387f, 442.992f, 121.246f, 442.746f, +120.352f, 441.504f, 66.2578f, 455.586f, 56.25f, 435.75f, 56.25f, 435.75f, 59.9062f, 456.168f, +119.25f, 443.75f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, +5, 119.25f, 443.75f, 119.25f, 443.75f, 121.387f, 442.992f, 121.246f, 442.746f, 120.352f, +441.504f, 66.2578f, 455.586f, 56.25f, 435.75f, 56.25f, 435.75f, 59.9062f, 456.168f, 119.25f, +443.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +116.246f, 432.75f, 116.246f, 432.75f, 118.539f, 432.383f, 118.25f, 431.746f, 118.023f, 430.641f, +62.25f, 426.965f, 58.25f, 404.75f, 58.25f, 404.75f, 56.0391f, 425.516f, 116.246f, 432.75f, +10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 116.246f, +432.75f, 116.246f, 432.75f, 118.539f, 432.383f, 118.25f, 431.746f, 118.023f, 430.641f, 62.25f, +426.965f, 58.25f, 404.75f, 58.25f, 404.75f, 56.0391f, 425.516f, 116.246f, 432.75f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 112.25f, 438.746f, +112.25f, 438.742f, 113.82f, 438.164f, 113.25f, 437.75f, 113.059f, 436.52f, 57.3437f, 441.016f, +50.2461f, 419.75f, 50.2461f, 419.75f, 50.9883f, 440.492f, 112.25f, 438.746f, 10, 0.1892f, +0, 0, 0, 0, 0, 0, 5, 112.25f, 438.746f, 112.25f, +438.742f, 113.82f, 438.164f, 113.25f, 437.75f, 113.059f, 436.52f, 57.3437f, 441.016f, 50.2461f, +419.75f, 50.2461f, 419.75f, 50.9883f, 440.492f, 112.25f, 438.746f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 100.246f, 458.746f, 100.246f, 458.746f, +101.527f, 457.406f, 101.25f, 456.746f, 100.121f, 456.262f, 52.0039f, 484.699f, 36.25f, 467.746f, +36.25f, 467.746f, 46.0586f, 487.012f, 100.246f, 458.746f, 10, 0.1892f, 0, 0, +0, 0, 0, 0, 5, 100.246f, 458.746f, 100.246f, 458.746f, 101.527f, +457.406f, 101.25f, 456.746f, 100.121f, 456.262f, 52.0039f, 484.699f, 36.25f, 467.746f, 36.25f, +467.746f, 46.0586f, 487.012f, 100.246f, 458.746f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 92.2461f, 454.75f, 92.2422f, 454.75f, 93.3906f, 452.969f, +93.2461f, 452.75f, 92.125f, 451.672f, 41.0976f, 474.484f, 27.25f, 456.746f, 27.25f, 456.746f, +34.9258f, 476.105f, 92.2461f, 454.75f, 10, 0.1892f, 0, 0, 0, 0, +0, 0, 5, 92.2461f, 454.75f, 92.2422f, 454.75f, 93.3906f, 452.969f, 93.2461f, +452.75f, 92.125f, 451.672f, 41.0976f, 474.484f, 27.25f, 456.746f, 27.25f, 456.746f, 34.9258f, +476.105f, 92.2461f, 454.75f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 89.2461f, 449.746f, 89.2461f, 449.742f, 90.6992f, 448.723f, 90.25f, 447.746f, +89.6211f, 447.262f, 35.9609f, 462.906f, 25.25f, 442.746f, 25.25f, 442.742f, 29.625f, 463.676f, +89.2461f, 449.746f, 10, 0.1892f, 0, 0, 0, 0, 0, 0, +5, 89.2461f, 449.746f, 89.2461f, 449.742f, 90.6992f, 448.723f, 90.25f, 447.746f, 89.6211f, +447.262f, 35.9609f, 462.906f, 25.25f, 442.746f, 25.25f, 442.742f, 29.625f, 463.676f, 89.2461f, +449.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +100.246f, 448.75f, 100.246f, 448.75f, 101.969f, 447.469f, 101.25f, 446.75f, 100.43f, 446.512f, +56.3516f, 480.887f, 39.2461f, 466.746f, 39.2461f, 466.742f, 50.75f, 483.941f, 100.246f, 448.75f, +10, 0.1892f, 0, 0, 0, 0, 0, 0, 5, 100.246f, +448.75f, 100.246f, 448.75f, 101.969f, 447.469f, 101.25f, 446.75f, 100.43f, 446.512f, 56.3516f, +480.887f, 39.2461f, 466.746f, 39.2461f, 466.742f, 50.75f, 483.941f, 100.246f, 448.75f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 79.25f, 480.746f, +79.25f, 480.746f, 79.6367f, 479.02f, 79.25f, 478.746f, 77.8789f, 478.578f, 46.418f, 524.777f, +25.25f, 516.75f, 25.25f, 516.75f, 42.0195f, 529.398f, 79.25f, 480.746f, 10, 0.1892f, +0, 0, 0, 0, 0, 0, 5, 79.25f, 480.746f, 79.25f, +480.746f, 79.6367f, 479.02f, 79.25f, 478.746f, 77.8789f, 478.578f, 46.418f, 524.777f, 25.25f, +516.75f, 25.25f, 516.75f, 42.0195f, 529.398f, 79.25f, 480.746f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 79.25f, 473.746f, 79.25f, 473.742f, +80.8164f, 471.527f, 80.25f, 470.75f, 79.1914f, 470.723f, 38.5078f, 509.051f, 19.25f, 496.75f, +19.25f, 496.75f, 33.2148f, 512.609f, 79.25f, 473.746f, 10, 0.1892f, 0, 0, +0, 0, 0, 0, 5, 79.25f, 473.746f, 79.25f, 473.742f, 80.8164f, +471.527f, 80.25f, 470.75f, 79.1914f, 470.723f, 38.5078f, 509.051f, 19.25f, 496.75f, 19.25f, +496.75f, 33.2148f, 512.609f, 79.25f, 473.746f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 79.25f, 468.75f, 79.25f, 468.75f, 80.8516f, 466.828f, +80.25f, 466.746f, 79.3086f, 465.875f, 35.2305f, 500.246f, 17.25f, 485.75f, 17.25f, 485.75f, +29.6289f, 503.301f, 79.25f, 468.75f, 10, 0.1892f, 0, 0, 0, 0, +0, 0, 5, 79.25f, 468.75f, 79.25f, 468.75f, 80.8516f, 466.828f, 80.25f, +466.746f, 79.3086f, 465.875f, 35.2305f, 500.246f, 17.25f, 485.75f, 17.25f, 485.75f, 29.6289f, +503.301f, 79.25f, 468.75f, 10, 0, 1, 1, 1, 1, 1, +1, 88, 77.2461f, 466.746f, 77.7383f, 459.973f, 78.8242f, 452.746f, 80.25f, 449.746f, +80.25f, 449.742f, 76.7773f, 435.676f, 86.25f, 420.746f, 86.25f, 420.742f, 86.0195f, 413.238f, +88.2461f, 409.746f, 88.2461f, 409.742f, 92.1797f, 400.477f, 97.25f, 399.75f, 101.73f, 398.887f, +111.324f, 395.508f, 122.246f, 393.75f, 122.246f, 393.75f, 141.02f, 378.477f, 137.246f, 364.75f, +137.242f, 364.75f, 137.059f, 346.355f, 133.246f, 344.75f, 133.246f, 344.75f, 145.859f, 356.918f, +135.25f, 338.75f, 130.25f, 317.746f, 130.25f, 317.742f, 158.617f, 341.516f, 141.25f, 321.746f, +130.25f, 292.75f, 130.25f, 292.75f, 152.02f, 312.918f, 144.246f, 303.75f, 140.25f, 293.746f, +140.25f, 293.746f, 188.098f, 323.918f, 154.246f, 291.746f, 154.242f, 291.746f, 163.02f, 295.316f, +168.25f, 291.746f, 168.25f, 291.746f, 175.34f, 293.559f, 174.25f, 291.746f, 174.25f, 291.746f, +151.578f, 280.355f, 147.25f, 259.746f, 147.25f, 259.746f, 156.859f, 271.117f, 153.246f, 258.746f, +154.246f, 246.746f, 154.242f, 246.746f, 158.18f, 270.238f, 157.25f, 228.746f, 157.25f, 228.742f, +178.859f, 248.676f, 166.246f, 225.746f, 166.246f, 207.75f, 166.246f, 207.75f, 182.816f, 225.355f, +176.246f, 211.75f, 176.246f, 211.75f, 186.777f, 220.957f, 182.25f, 203.746f, 182.25f, 203.746f, +181.5f, 192.797f, 186.25f, 204.746f, 186.25f, 204.746f, 203.938f, 238.777f, 197.25f, 209.75f, +197.25f, 209.75f, 196.457f, 188.836f, 201.246f, 204.746f, 201.246f, 204.746f, 202.18f, 193.676f, +212.246f, 185.75f, 212.246f, 185.75f, 210.977f, 241.637f, 225.25f, 201.746f, 229.25f, 183.746f, +229.25f, 183.742f, 232.539f, 194.117f, 232.246f, 199.75f, 232.246f, 199.75f, 248.379f, 217.879f, +241.25f, 190.746f, 241.25f, 190.746f, 257.617f, 216.117f, 254.246f, 201.746f, 254.246f, 201.746f, +245.738f, 183.996f, 247.246f, 178.75f, 247.242f, 178.75f, 265.977f, 216.996f, 267.246f, 218.75f, +267.246f, 218.75f, 265.098f, 172.117f, 277.246f, 211.75f, 277.246f, 211.75f, 283.137f, 198.516f, +280.246f, 193.746f, 280.242f, 193.746f, 288.859f, 202.477f, 288.246f, 205.746f, 288.246f, 205.742f, +293.039f, 215.016f, 296.25f, 199.75f, 296.25f, 199.75f, 298.098f, 189.719f, 300.246f, 192.746f, +300.246f, 192.746f, 304.258f, 166.836f, 305.25f, 191.746f, 305.25f, 191.746f, 307.34f, 206.879f, +299.246f, 219.75f, 299.246f, 219.75f, 300.297f, 223.156f, 297.246f, 227.746f, 297.246f, 227.742f, +312.18f, 203.797f, 304.25f, 235.746f, 304.25f, 235.746f, 316.578f, 226.676f, 318.25f, 226.746f, +318.25f, 226.746f, 302.937f, 252.195f, 312.246f, 246.746f, 312.242f, 246.746f, 306.898f, 258.355f, +326.25f, 244.75f, 326.25f, 244.75f, 309.098f, 262.758f, 328.25f, 251.75f, 328.25f, 251.75f, +337.258f, 245.156f, 329.25f, 255.75f, 329.25f, 255.75f, 313.059f, 273.758f, 337.25f, 253.75f, +337.25f, 253.75f, 350.02f, 235.918f, 351.25f, 232.75f, 351.25f, 232.75f, 339.898f, 264.957f, +335.246f, 267.75f, 335.242f, 267.75f, 344.301f, 308.078f, 389.246f, 290.75f, 389.246f, 290.75f, +397.098f, 271.996f, 402.246f, 291.746f, 402.242f, 291.746f, 416.02f, 299.277f, 428.25f, 268.75f, +428.25f, 268.75f, 432.738f, 283.879f, 432.246f, 286.746f, 432.246f, 286.742f, 439.34f, 285.637f, +438.25f, 286.746f, 438.25f, 286.742f, 452.98f, 282.117f, 454.246f, 282.746f, 454.246f, 282.746f, +461.777f, 275.516f, 462.246f, 279.75f, 462.242f, 279.75f, 472.34f, 276.398f, 470.25f, 280.746f, +470.25f, 280.746f, 479.82f, 263.195f, 480.246f, 258.746f, 483.25f, 274.75f, 485.25f, 271.746f, +485.25f, 271.746f, 486.859f, 279.918f, 486.25f, 280.746f, 485.098f, 282.559f, 507.98f, 273.758f, +513.246f, 250.746f, 515.246f, 241.75f, 515.242f, 241.75f, 522.059f, 257.918f, 520.246f, 262.75f, +520.246f, 262.75f, 526.02f, 261.438f, 526.246f, 256.746f, 526.242f, 256.746f, 530.859f, 282.117f, +525.25f, 288.746f, 525.25f, 288.742f, 530.418f, 289.598f, 531.246f, 285.75f, 531.246f, 293.746f, +531.246f, 293.746f, 539.66f, 292.676f, 539.246f, 295.746f, 539.242f, 295.742f, 544.5f, 299.719f, +546.246f, 294.75f, 546.242f, 294.75f, 533.059f, 333.156f, 553.246f, 311.75f, 553.246f, 311.75f, +561.219f, 300.156f, 557.246f, 320.75f, 553.301f, 341.516f, 548.898f, 343.277f, 554.25f, 343.746f, +554.25f, 343.742f, 555.059f, 347.676f, 553.246f, 349.75f, 550.66f, 351.195f, 554.25f, 349.75f, +554.25f, 349.75f, 554.25f, 349.75f, 559.461f, 345.035f, 553.246f, 368.746f, 553.246f, 368.746f, +560.777f, 367.477f, 547.25f, 399.75f, 547.25f, 399.75f, 550.66f, 402.238f, 546.246f, 411.746f, +546.242f, 411.742f, 555.059f, 406.637f, 558.25f, 408.75f, 558.25f, 408.75f, 557.699f, 410.156f, +554.25f, 414.746f, 554.25f, 414.746f, 530.418f, 474.84f, 553.246f, 450.75f, 553.246f, 450.75f, +565.895f, 435.73f, 559.246f, 460.746f, 559.242f, 460.742f, 548.832f, 487.223f, 549.25f, 491.746f, +77.2461f, 466.746f, 10, 1.1f, 0, 0, 0, 0, 0, 0, +88, 77.2461f, 466.746f, 77.7383f, 459.973f, 78.8242f, 452.746f, 80.25f, 449.746f, 80.25f, +449.742f, 76.7773f, 435.676f, 86.25f, 420.746f, 86.25f, 420.742f, 86.0195f, 413.238f, 88.2461f, +409.746f, 88.2461f, 409.742f, 92.1797f, 400.477f, 97.25f, 399.75f, 101.73f, 398.887f, 111.324f, +395.508f, 122.246f, 393.75f, 122.246f, 393.75f, 141.02f, 378.477f, 137.246f, 364.75f, 137.242f, +364.75f, 137.059f, 346.355f, 133.246f, 344.75f, 133.246f, 344.75f, 145.859f, 356.918f, 135.25f, +338.75f, 130.25f, 317.746f, 130.25f, 317.742f, 158.617f, 341.516f, 141.25f, 321.746f, 130.25f, +292.75f, 130.25f, 292.75f, 152.02f, 312.918f, 144.246f, 303.75f, 140.25f, 293.746f, 140.25f, +293.746f, 188.098f, 323.918f, 154.246f, 291.746f, 154.242f, 291.746f, 163.02f, 295.316f, 168.25f, +291.746f, 168.25f, 291.746f, 175.34f, 293.559f, 174.25f, 291.746f, 174.25f, 291.746f, 151.578f, +280.355f, 147.25f, 259.746f, 147.25f, 259.746f, 156.859f, 271.117f, 153.246f, 258.746f, 154.246f, +246.746f, 154.242f, 246.746f, 158.18f, 270.238f, 157.25f, 228.746f, 157.25f, 228.742f, 178.859f, +248.676f, 166.246f, 225.746f, 166.246f, 207.75f, 166.246f, 207.75f, 182.816f, 225.355f, 176.246f, +211.75f, 176.246f, 211.75f, 186.777f, 220.957f, 182.25f, 203.746f, 182.25f, 203.746f, 181.5f, +192.797f, 186.25f, 204.746f, 186.25f, 204.746f, 203.938f, 238.777f, 197.25f, 209.75f, 197.25f, +209.75f, 196.457f, 188.836f, 201.246f, 204.746f, 201.246f, 204.746f, 202.18f, 193.676f, 212.246f, +185.75f, 212.246f, 185.75f, 210.977f, 241.637f, 225.25f, 201.746f, 229.25f, 183.746f, 229.25f, +183.742f, 232.539f, 194.117f, 232.246f, 199.75f, 232.246f, 199.75f, 248.379f, 217.879f, 241.25f, +190.746f, 241.25f, 190.746f, 257.617f, 216.117f, 254.246f, 201.746f, 254.246f, 201.746f, 245.738f, +183.996f, 247.246f, 178.75f, 247.242f, 178.75f, 265.977f, 216.996f, 267.246f, 218.75f, 267.246f, +218.75f, 265.098f, 172.117f, 277.246f, 211.75f, 277.246f, 211.75f, 283.137f, 198.516f, 280.246f, +193.746f, 280.242f, 193.746f, 288.859f, 202.477f, 288.246f, 205.746f, 288.246f, 205.742f, 293.039f, +215.016f, 296.25f, 199.75f, 296.25f, 199.75f, 298.098f, 189.719f, 300.246f, 192.746f, 300.246f, +192.746f, 304.258f, 166.836f, 305.25f, 191.746f, 305.25f, 191.746f, 307.34f, 206.879f, 299.246f, +219.75f, 299.246f, 219.75f, 300.297f, 223.156f, 297.246f, 227.746f, 297.246f, 227.742f, 312.18f, +203.797f, 304.25f, 235.746f, 304.25f, 235.746f, 316.578f, 226.676f, 318.25f, 226.746f, 318.25f, +226.746f, 302.937f, 252.195f, 312.246f, 246.746f, 312.242f, 246.746f, 306.898f, 258.355f, 326.25f, +244.75f, 326.25f, 244.75f, 309.098f, 262.758f, 328.25f, 251.75f, 328.25f, 251.75f, 337.258f, +245.156f, 329.25f, 255.75f, 329.25f, 255.75f, 313.059f, 273.758f, 337.25f, 253.75f, 337.25f, +253.75f, 350.02f, 235.918f, 351.25f, 232.75f, 351.25f, 232.75f, 339.898f, 264.957f, 335.246f, +267.75f, 335.242f, 267.75f, 344.301f, 308.078f, 389.246f, 290.75f, 389.246f, 290.75f, 397.098f, +271.996f, 402.246f, 291.746f, 402.242f, 291.746f, 416.02f, 299.277f, 428.25f, 268.75f, 428.25f, +268.75f, 432.738f, 283.879f, 432.246f, 286.746f, 432.246f, 286.742f, 439.34f, 285.637f, 438.25f, +286.746f, 438.25f, 286.742f, 452.98f, 282.117f, 454.246f, 282.746f, 454.246f, 282.746f, 461.777f, +275.516f, 462.246f, 279.75f, 462.242f, 279.75f, 472.34f, 276.398f, 470.25f, 280.746f, 470.25f, +280.746f, 479.82f, 263.195f, 480.246f, 258.746f, 483.25f, 274.75f, 485.25f, 271.746f, 485.25f, +271.746f, 486.859f, 279.918f, 486.25f, 280.746f, 485.098f, 282.559f, 507.98f, 273.758f, 513.246f, +250.746f, 515.246f, 241.75f, 515.242f, 241.75f, 522.059f, 257.918f, 520.246f, 262.75f, 520.246f, +262.75f, 526.02f, 261.438f, 526.246f, 256.746f, 526.242f, 256.746f, 530.859f, 282.117f, 525.25f, +288.746f, 525.25f, 288.742f, 530.418f, 289.598f, 531.246f, 285.75f, 531.246f, 293.746f, 531.246f, +293.746f, 539.66f, 292.676f, 539.246f, 295.746f, 539.242f, 295.742f, 544.5f, 299.719f, 546.246f, +294.75f, 546.242f, 294.75f, 533.059f, 333.156f, 553.246f, 311.75f, 553.246f, 311.75f, 561.219f, +300.156f, 557.246f, 320.75f, 553.301f, 341.516f, 548.898f, 343.277f, 554.25f, 343.746f, 554.25f, +343.742f, 555.059f, 347.676f, 553.246f, 349.75f, 550.66f, 351.195f, 554.25f, 349.75f, 554.25f, +349.75f, 554.25f, 349.75f, 559.461f, 345.035f, 553.246f, 368.746f, 553.246f, 368.746f, 560.777f, +367.477f, 547.25f, 399.75f, 547.25f, 399.75f, 550.66f, 402.238f, 546.246f, 411.746f, 546.242f, +411.742f, 555.059f, 406.637f, 558.25f, 408.75f, 558.25f, 408.75f, 557.699f, 410.156f, 554.25f, +414.746f, 554.25f, 414.746f, 530.418f, 474.84f, 553.246f, 450.75f, 553.246f, 450.75f, 565.895f, +435.73f, 559.246f, 460.746f, 559.242f, 460.742f, 548.832f, 487.223f, 549.25f, 491.746f, 77.2461f, +466.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 44, +549.25f, 491.746f, 550.379f, 491.531f, 552.805f, 490.293f, 554.25f, 488.746f, 554.25f, 488.742f, +561.66f, 476.598f, 556.25f, 496.75f, 556.25f, 496.75f, 545.82f, 528.52f, 555.246f, 515.746f, +555.246f, 515.742f, 562.098f, 508.277f, 558.25f, 522.746f, 554.328f, 541.309f, 551.25f, 548.746f, +551.25f, 548.746f, 551.25f, 548.746f, 564.301f, 543.039f, 535.246f, 586.75f, 544.246f, 582.75f, +544.246f, 582.75f, 522.938f, 626.199f, 499.25f, 631.746f, 490.25f, 638.746f, 490.25f, 638.742f, +532.621f, 680.316f, 518.25f, 720.75f, 518.25f, 720.75f, 511.059f, 726.52f, 500.246f, 716.75f, +500.246f, 716.75f, 493.461f, 711.117f, 487.246f, 712.75f, 487.246f, 712.75f, 452.98f, 711.559f, +451.25f, 711.746f, 448.578f, 711.559f, 410.301f, 752.477f, 338.25f, 732.746f, 338.25f, 732.742f, +332.418f, 730.918f, 327.25f, 731.75f, 327.25f, 731.75f, 307.34f, 749.84f, 253.246f, 724.746f, +253.246f, 724.746f, 242.656f, 722.559f, 241.25f, 722.746f, 239.137f, 722.559f, 236.059f, 722.559f, +227.25f, 715.746f, 218.457f, 708.477f, 218.02f, 707.598f, 216.25f, 705.75f, 216.25f, 705.75f, +197.777f, 693.52f, 192.25f, 692.75f, 192.25f, 692.75f, 179.738f, 685.598f, 175.25f, 674.75f, +171.246f, 673.746f, 171.246f, 673.742f, 169.18f, 665.359f, 168.25f, 663.75f, 168.25f, 663.75f, +163.457f, 660.078f, 162.25f, 653.746f, 162.25f, 653.742f, 152.898f, 647.316f, 153.246f, 642.746f, +153.242f, 642.742f, 151.578f, 636.758f, 150.246f, 631.746f, 150.246f, 631.742f, 142.777f, 626.199f, +143.246f, 622.75f, 143.242f, 622.75f, 135.297f, 607.719f, 136.25f, 599.75f, 136.25f, 599.75f, +129.578f, 600.68f, 126.246f, 597.75f, 126.242f, 597.75f, 125.617f, 592.758f, 124.25f, 592.746f, +124.25f, 592.746f, 120.777f, 591, 123.25f, 586.75f, 123.25f, 586.75f, 121.656f, 583.52f, +121.246f, 581.746f, 121.246f, 581.746f, 122.098f, 578.68f, 117.25f, 572.746f, 117.25f, 572.742f, +110.219f, 551.84f, 112.25f, 545.75f, 112.25f, 545.75f, 112.859f, 540.84f, 110.246f, 538.75f, +110.246f, 538.75f, 105.816f, 539.52f, 115.246f, 526.746f, 115.242f, 526.742f, 115.938f, 525, +112.25f, 522.746f, 112.25f, 522.746f, 93.5f, 518.398f, 91.25f, 500.746f, 91.25f, 500.746f, +75.8984f, 484.078f, 76.2461f, 478.746f, 75.8984f, 475.824f, 76.1953f, 472.359f, 77.2461f, 467.746f, +77.2461f, 467.746f, 76.3398f, 458.117f, 106.25f, 456.746f, 137.059f, 456.355f, 549.25f, 491.746f, +549.25f, 491.746f, 10, 1.1f, 0, 0, 0, 0, 0, 0, +44, 549.25f, 491.746f, 550.379f, 491.531f, 552.805f, 490.293f, 554.25f, 488.746f, 554.25f, +488.742f, 561.66f, 476.598f, 556.25f, 496.75f, 556.25f, 496.75f, 545.82f, 528.52f, 555.246f, +515.746f, 555.246f, 515.742f, 562.098f, 508.277f, 558.25f, 522.746f, 554.328f, 541.309f, 551.25f, +548.746f, 551.25f, 548.746f, 551.25f, 548.746f, 564.301f, 543.039f, 535.246f, 586.75f, 544.246f, +582.75f, 544.246f, 582.75f, 522.938f, 626.199f, 499.25f, 631.746f, 490.25f, 638.746f, 490.25f, +638.742f, 532.621f, 680.316f, 518.25f, 720.75f, 518.25f, 720.75f, 511.059f, 726.52f, 500.246f, +716.75f, 500.246f, 716.75f, 493.461f, 711.117f, 487.246f, 712.75f, 487.246f, 712.75f, 452.98f, +711.559f, 451.25f, 711.746f, 448.578f, 711.559f, 410.301f, 752.477f, 338.25f, 732.746f, 338.25f, +732.742f, 332.418f, 730.918f, 327.25f, 731.75f, 327.25f, 731.75f, 307.34f, 749.84f, 253.246f, +724.746f, 253.246f, 724.746f, 242.656f, 722.559f, 241.25f, 722.746f, 239.137f, 722.559f, 236.059f, +722.559f, 227.25f, 715.746f, 218.457f, 708.477f, 218.02f, 707.598f, 216.25f, 705.75f, 216.25f, +705.75f, 197.777f, 693.52f, 192.25f, 692.75f, 192.25f, 692.75f, 179.738f, 685.598f, 175.25f, +674.75f, 171.246f, 673.746f, 171.246f, 673.742f, 169.18f, 665.359f, 168.25f, 663.75f, 168.25f, +663.75f, 163.457f, 660.078f, 162.25f, 653.746f, 162.25f, 653.742f, 152.898f, 647.316f, 153.246f, +642.746f, 153.242f, 642.742f, 151.578f, 636.758f, 150.246f, 631.746f, 150.246f, 631.742f, 142.777f, +626.199f, 143.246f, 622.75f, 143.242f, 622.75f, 135.297f, 607.719f, 136.25f, 599.75f, 136.25f, +599.75f, 129.578f, 600.68f, 126.246f, 597.75f, 126.242f, 597.75f, 125.617f, 592.758f, 124.25f, +592.746f, 124.25f, 592.746f, 120.777f, 591, 123.25f, 586.75f, 123.25f, 586.75f, 121.656f, +583.52f, 121.246f, 581.746f, 121.246f, 581.746f, 122.098f, 578.68f, 117.25f, 572.746f, 117.25f, +572.742f, 110.219f, 551.84f, 112.25f, 545.75f, 112.25f, 545.75f, 112.859f, 540.84f, 110.246f, +538.75f, 110.246f, 538.75f, 105.816f, 539.52f, 115.246f, 526.746f, 115.242f, 526.742f, 115.938f, +525, 112.25f, 522.746f, 112.25f, 522.746f, 93.5f, 518.398f, 91.25f, 500.746f, 91.25f, +500.746f, 75.8984f, 484.078f, 76.2461f, 478.746f, 75.8984f, 475.824f, 76.1953f, 472.359f, 77.2461f, +467.746f, 77.2461f, 467.746f, 76.3398f, 458.117f, 106.25f, 456.746f, 137.059f, 456.355f, 549.25f, +491.746f, 549.25f, 491.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, +0.15f, 18, 93.2461f, 466.746f, 65.3398f, 510.477f, 81.2461f, 448.75f, 81.2461f, 448.75f, +90.8594f, 410.598f, 233.246f, 451.746f, 233.246f, 451.746f, 233.246f, 451.742f, 419.098f, 485.398f, +431.246f, 489.746f, 443.738f, 494.199f, 548.246f, 486.746f, 548.246f, 486.746f, 542.246f, 505.75f, +471.02f, 556.68f, 449.898f, 531.156f, 435.246f, 535.746f, 419.98f, 539.957f, 422.621f, 529.398f, +419.246f, 528.746f, 415.578f, 527.637f, 372.461f, 554.918f, 365.246f, 553.75f, 358.379f, 553.156f, +330.504f, 579.285f, 347.25f, 544.746f, 364.539f, 506.957f, 282.699f, 501.238f, 264.246f, 513.746f, +245.738f, 525.879f, 272.25f, 493.746f, 272.25f, 493.746f, 292.379f, 471.316f, 254.246f, 489.746f, +254.246f, 489.746f, 216.699f, 503.879f, 190.297f, 475.719f, 187.246f, 474.75f, 183.258f, 473.957f, +177.977f, 470.438f, 177.246f, 477.746f, 176.219f, 484.52f, 167.957f, 502.891f, 133.246f, 473.746f, +111.098f, 455.695f, 96.25f, 479.75f, 96.25f, 479.75f, 93.2461f, 466.746f, 10, 0, +0.91f, 0.5f, 0.228f, 0.91f, 0.5f, 0.228f, 19, 367.246f, 551.75f, 359.82f, +551.238f, 331.914f, 577.352f, 348.25f, 542.75f, 366.641f, 503.719f, 284.141f, 499.316f, 265.246f, +511.746f, 247.18f, 523.957f, 273.25f, 491.746f, 273.25f, 491.746f, 293.82f, 469.398f, 256.246f, +487.75f, 256.246f, 487.75f, 218.137f, 501.957f, 191.738f, 473.797f, 188.246f, 472.75f, 184.699f, +472.039f, 179.418f, 468.516f, 178.246f, 475.746f, 177.656f, 482.598f, 169.543f, 500.785f, 134.25f, +471.746f, 111.18f, 452.957f, 96.25f, 476.75f, 96.25f, 476.75f, 93.2461f, 465.75f, 65.3164f, +509.219f, 82.2461f, 444.746f, 82.2461f, 444.746f, 91.5781f, 407.238f, 235.246f, 449.746f, 235.246f, +449.746f, 235.242f, 449.742f, 420.539f, 483.477f, 433.246f, 487.75f, 445.18f, 492.277f, 549.25f, +485.75f, 549.25f, 485.75f, 543.25f, 504.746f, 471.578f, 555.398f, 451.34f, 529.238f, 436.25f, +533.746f, 421.418f, 538.039f, 424.059f, 527.477f, 420.246f, 526.746f, 417.02f, 525.719f, 373.898f, +552.996f, 367.246f, 551.75f, 10, 0, 0.919f, 0.55f, 0.305f, 0.919f, 0.55f, +0.305f, 19, 368.246f, 549.75f, 361.258f, 549.316f, 334.051f, 575.75f, 350.25f, 540.75f, +367.641f, 500.695f, 285.578f, 497.398f, 267.246f, 509.75f, 248.617f, 522.035f, 275.246f, 489.746f, +275.246f, 489.746f, 295.258f, 467.477f, 257.246f, 485.75f, 257.246f, 485.75f, 219.578f, 500.035f, +193.18f, 471.875f, 189.246f, 470.75f, 186.137f, 470.117f, 180.859f, 466.598f, 180.246f, 473.746f, +179.098f, 480.676f, 171.125f, 498.68f, 136.25f, 469.746f, 111.258f, 450.215f, 97.25f, 472.75f, +97.25f, 472.75f, 93.2461f, 463.75f, 66.6172f, 506.637f, 82.2461f, 441.75f, 82.2461f, 441.75f, +92.2969f, 403.879f, 236.25f, 447.746f, 236.25f, 447.746f, 236.25f, 447.746f, 421.98f, 481.559f, +434.246f, 485.75f, 446.617f, 490.355f, 549.25f, 483.75f, 549.25f, 483.75f, 543.25f, 502.746f, +472.141f, 554.117f, 452.777f, 527.316f, 438.25f, 531.75f, 422.859f, 536.117f, 425.5f, 525.559f, +422.246f, 524.746f, 418.457f, 523.797f, 375.34f, 551.078f, 368.246f, 549.75f, 10, 0, +0.928f, 0.6f, 0.382f, 0.928f, 0.6f, 0.382f, 19, 369.25f, 548.746f, 362.699f, +547.398f, 335.496f, 573.832f, 351.25f, 538.75f, 369.738f, 497.285f, 286.43f, 495.867f, 268.246f, +507.75f, 250.059f, 520.117f, 276.246f, 487.75f, 276.246f, 487.75f, 296.699f, 465.559f, 259.25f, +483.75f, 259.25f, 483.75f, 221.02f, 498.117f, 194.617f, 469.957f, 191.246f, 468.75f, 187.578f, +468.199f, 182.301f, 464.676f, 181.25f, 471.746f, 180.539f, 478.758f, 172.711f, 496.574f, 137.246f, +467.746f, 111.336f, 447.477f, 97.25f, 469.746f, 97.25f, 469.746f, 93.2461f, 461.75f, 68.7969f, +502.516f, 83.2461f, 438.746f, 83.2461f, 438.746f, 93.0195f, 400.516f, 237.25f, 445.746f, 237.25f, +445.746f, 237.25f, 445.746f, 423.418f, 479.637f, 435.246f, 483.75f, 448.059f, 488.438f, 550.246f, +481.75f, 550.246f, 481.75f, 544.246f, 501.75f, 472.699f, 552.84f, 454.219f, 525.398f, 439.25f, +529.75f, 424.301f, 534.199f, 426.938f, 523.637f, 423.246f, 522.746f, 419.898f, 521.879f, 376.777f, +549.156f, 369.25f, 548.746f, 10, 0, 0.937f, 0.65f, 0.46f, 0.937f, 0.65f, +0.46f, 19, 371.25f, 546.746f, 364.141f, 545.477f, 337.492f, 572.156f, 352.25f, 536.75f, +371.18f, 493.559f, 288.457f, 493.559f, 270.25f, 505.75f, 251.5f, 518.195f, 278.246f, 485.75f, +278.246f, 485.75f, 298.141f, 463.637f, 260.25f, 481.75f, 260.25f, 481.75f, 222.457f, 496.195f, +196.059f, 468.035f, 192.25f, 466.746f, 189.02f, 466.277f, 183.738f, 462.758f, 183.25f, 469.746f, +181.98f, 476.836f, 174.297f, 494.473f, 139.246f, 466.746f, 111.418f, 444.738f, 97.25f, 466.746f, +97.25f, 466.746f, 93.2461f, 460.746f, 70.9766f, 498.617f, 84.25f, 434.746f, 84.25f, 434.746f, +93.7383f, 397.156f, 239.25f, 444.746f, 239.25f, 444.746f, 239.25f, 444.742f, 424.859f, 477.715f, +437.25f, 481.75f, 449.5f, 486.516f, 550.246f, 479.75f, 550.246f, 479.75f, 544.246f, 500.746f, +473.262f, 551.559f, 455.66f, 523.477f, 440.25f, 527.75f, 425.738f, 532.277f, 428.379f, 521.715f, +425.25f, 520.75f, 421.34f, 519.957f, 378.219f, 547.238f, 371.25f, 546.746f, 10, 0, +0.946f, 0.7f, 0.537f, 0.946f, 0.7f, 0.537f, 19, 372.25f, 544.746f, 365.578f, +543.559f, 337.02f, 569.352f, 354.246f, 534.75f, 375.258f, 492.078f, 289.898f, 491.637f, 271.25f, +503.75f, 252.938f, 516.277f, 279.246f, 483.75f, 279.246f, 483.75f, 299.578f, 461.719f, 261.25f, +479.75f, 261.25f, 479.75f, 223.898f, 494.277f, 197.5f, 466.117f, 194.25f, 464.746f, 190.457f, +464.355f, 185.18f, 460.836f, 184.25f, 467.746f, 183.418f, 474.918f, 175.879f, 492.367f, 140.25f, +464.746f, 111.5f, 441.996f, 98.2461f, 462.746f, 98.2461f, 462.746f, 92.2461f, 458.746f, 72.9375f, +495.156f, 85.25f, 431.746f, 85.25f, 431.746f, 94.457f, 393.797f, 240.25f, 442.746f, 240.25f, +442.746f, 240.25f, 442.742f, 426.301f, 475.797f, 438.25f, 479.75f, 450.941f, 484.598f, 551.25f, +477.746f, 551.25f, 477.746f, 545.25f, 498.75f, 473.82f, 550.277f, 457.102f, 521.559f, 442.246f, +525.75f, 427.18f, 530.355f, 429.82f, 519.797f, 426.25f, 518.75f, 422.781f, 518.039f, 379.66f, +545.316f, 372.25f, 544.746f, 10, 0, 0.955f, 0.75f, 0.614f, 0.955f, 0.75f, +0.614f, 19, 374.25f, 542.75f, 367.02f, 541.637f, 338.043f, 567.223f, 355.246f, 532.746f, +378.02f, 488.836f, 291.34f, 489.715f, 273.25f, 501.75f, 254.379f, 514.355f, 281.25f, 481.75f, +281.25f, 481.75f, 301.02f, 459.797f, 263.25f, 478.746f, 263.25f, 478.746f, 225.34f, 492.355f, +198.938f, 464.195f, 195.25f, 463.75f, 191.898f, 462.438f, 186.617f, 458.918f, 185.25f, 465.75f, +184.859f, 472.996f, 177.465f, 490.262f, 141.25f, 462.746f, 111.578f, 439.258f, 98.2461f, 459.75f, +98.2461f, 459.75f, 92.2461f, 456.746f, 75.1172f, 490.156f, 85.25f, 428.75f, 85.25f, 428.75f, +95.1797f, 390.438f, 242.246f, 440.746f, 242.246f, 440.746f, 242.246f, 440.742f, 427.738f, 473.875f, +440.25f, 478.746f, 452.379f, 482.676f, 551.25f, 475.746f, 551.25f, 475.746f, 545.25f, 497.746f, +474.379f, 548.996f, 458.539f, 519.637f, 443.246f, 523.75f, 428.621f, 528.438f, 431.258f, 517.875f, +427.25f, 516.75f, 424.219f, 516.117f, 381.102f, 543.398f, 374.25f, 542.75f, 10, 0, +0.964f, 0.8f, 0.691f, 0.964f, 0.8f, 0.691f, 19, 375.246f, 540.75f, 368.461f, +539.719f, 338.273f, 564.66f, 357.246f, 530.746f, 381.219f, 487.355f, 292.777f, 487.797f, 274.25f, +499.746f, 255.82f, 512.438f, 282.25f, 479.75f, 282.25f, 479.75f, 302.457f, 457.879f, 264.246f, +476.75f, 264.246f, 476.75f, 226.777f, 490.438f, 200.379f, 462.277f, 197.25f, 461.75f, 193.34f, +460.516f, 188.059f, 456.996f, 187.246f, 463.75f, 186.297f, 471.078f, 179.047f, 488.156f, 143.246f, +460.746f, 111.656f, 436.516f, 99.2461f, 456.746f, 99.2461f, 456.746f, 92.2461f, 454.75f, 76.8555f, +486.477f, 86.25f, 424.75f, 86.25f, 424.75f, 95.8984f, 387.074f, 243.246f, 438.746f, 243.246f, +438.746f, 243.246f, 438.742f, 429.18f, 471.957f, 441.246f, 476.75f, 453.82f, 480.758f, 552.25f, +474.75f, 552.25f, 474.75f, 546.246f, 496.75f, 474.941f, 547.719f, 459.98f, 517.719f, 445.246f, +521.746f, 430.059f, 526.516f, 432.699f, 515.957f, 429.25f, 514.75f, 425.66f, 514.195f, 382.539f, +541.477f, 375.246f, 540.75f, 10, 0, 0.973f, 0.85f, 0.769f, 0.973f, 0.85f, +0.769f, 19, 377.246f, 538.75f, 369.898f, 537.797f, 339.715f, 562.738f, 358.25f, 528.746f, +382.66f, 485.437f, 294.219f, 485.875f, 275.246f, 497.746f, 257.258f, 510.516f, 283.25f, 477.746f, +283.25f, 477.746f, 303.898f, 455.957f, 266.246f, 474.75f, 266.246f, 474.75f, 228.219f, 488.516f, +201.816f, 460.355f, 198.246f, 459.75f, 194.777f, 458.598f, 189.5f, 455.078f, 188.246f, 461.75f, +187.738f, 469.156f, 180.633f, 486.051f, 144.246f, 458.746f, 111.738f, 433.777f, 99.2461f, 452.75f, +99.2461f, 452.75f, 92.2461f, 453.746f, 77.7188f, 482.578f, 87.2461f, 421.75f, 87.2461f, 421.75f, +96.6172f, 383.715f, 245.246f, 436.746f, 245.246f, 436.746f, 245.246f, 436.746f, 430.621f, 470.035f, +443.246f, 474.75f, 455.258f, 478.836f, 552.25f, 472.75f, 552.25f, 472.75f, 547.25f, 495.746f, +475.5f, 546.438f, 461.418f, 515.797f, 446.246f, 519.746f, 431.5f, 524.598f, 434.141f, 514.035f, +430.246f, 512.75f, 427.098f, 512.277f, 383.98f, 539.555f, 377.246f, 538.75f, 10, 0, +0.982f, 0.9f, 0.846f, 0.982f, 0.9f, 0.846f, 19, 378.246f, 536.75f, 371.34f, +535.879f, 341.578f, 561.055f, 360.25f, 526.746f, 384.098f, 482.195f, 295.66f, 483.957f, 277.246f, +496.75f, 258.699f, 508.598f, 285.25f, 475.746f, 285.25f, 475.746f, 305.34f, 454.035f, 267.246f, +472.75f, 267.246f, 472.75f, 229.66f, 486.598f, 203.258f, 458.438f, 199.246f, 457.75f, 196.219f, +456.676f, 190.937f, 453.156f, 190.246f, 459.75f, 189.18f, 467.238f, 182.219f, 483.949f, 146.25f, +456.746f, 111.82f, 431.035f, 99.2461f, 449.746f, 99.2461f, 449.746f, 92.2461f, 451.746f, 78.3594f, +478.238f, 87.2461f, 417.75f, 87.2461f, 417.75f, 97.3399f, 380.355f, 246.246f, 434.746f, 246.246f, +434.746f, 246.242f, 434.746f, 432.059f, 468.117f, 444.246f, 472.75f, 456.699f, 476.918f, 553.246f, +470.75f, 553.246f, 470.75f, 547.25f, 493.746f, 476.059f, 545.156f, 462.859f, 513.879f, 448.25f, +518.75f, 432.938f, 522.676f, 435.578f, 512.117f, 432.246f, 510.746f, 428.539f, 510.355f, 385.418f, +537.637f, 378.246f, 536.75f, 10, 0, 0.991f, 0.95f, 0.923f, 0.991f, 0.95f, +0.923f, 19, 380.25f, 534.75f, 372.777f, 533.957f, 344.207f, 559.746f, 361.25f, 524.746f, +384.66f, 478.078f, 297.098f, 482.035f, 278.246f, 494.75f, 260.141f, 506.676f, 286.246f, 473.746f, +286.246f, 473.746f, 306.777f, 452.117f, 269.246f, 470.75f, 269.246f, 470.75f, 231.098f, 484.676f, +204.699f, 456.516f, 201.246f, 455.746f, 197.66f, 454.758f, 192.379f, 451.238f, 191.246f, 458.746f, +190.621f, 465.316f, 183.801f, 481.844f, 147.25f, 454.75f, 111.898f, 428.297f, 100.246f, 446.75f, +100.246f, 446.75f, 92.2461f, 449.746f, 78.5586f, 475.656f, 88.2461f, 414.746f, 88.2461f, 414.746f, +98.0586f, 376.996f, 248.25f, 432.75f, 248.25f, 432.75f, 248.25f, 432.75f, 433.5f, 466.195f, +446.246f, 470.75f, 458.141f, 474.996f, 553.246f, 468.75f, 553.246f, 468.75f, 548.246f, 492.75f, +476.621f, 543.879f, 464.301f, 511.957f, 449.25f, 516.75f, 434.379f, 520.758f, 437.02f, 510.195f, +433.246f, 509.75f, 429.98f, 508.438f, 386.859f, 535.719f, 380.25f, 534.75f, 10, 0, +1, 1, 1, 1, 1, 1, 18, 92.2461f, 448.75f, 78.5391f, +472.637f, 89.2461f, 411.746f, 89.2461f, 411.746f, 98.7773f, 373.637f, 249.25f, 430.75f, 249.25f, +430.75f, 249.25f, 430.75f, 434.938f, 464.277f, 447.25f, 468.75f, 459.578f, 473.078f, 553.246f, +466.746f, 553.246f, 466.746f, 548.246f, 491.746f, 477.18f, 542.598f, 465.738f, 510.039f, 451.25f, +514.75f, 435.82f, 518.84f, 438.461f, 508.277f, 435.246f, 507.75f, 431.418f, 506.52f, 388.301f, +533.797f, 381.25f, 532.746f, 374.219f, 532.039f, 346.477f, 558.227f, 363.25f, 522.746f, 387.23f, +470.762f, 295.941f, 481.848f, 280.246f, 492.75f, 261.578f, 504.758f, 288.246f, 471.746f, 288.246f, +471.746f, 308.219f, 450.195f, 270.25f, 468.75f, 270.25f, 468.75f, 232.539f, 482.758f, 206.137f, +454.598f, 202.246f, 453.746f, 199.098f, 452.836f, 193.816f, 449.316f, 193.25f, 456.746f, 192.059f, +463.398f, 185.387f, 479.738f, 149.246f, 452.75f, 111.977f, 425.559f, 100.246f, 442.746f, 100.246f, +442.746f, 92.2461f, 448.75f, 10, 0, 0, 0, 0, 0, 0, +0, 7, 138.246f, 415.75f, 138.246f, 415.75f, 130.457f, 402.676f, 153.246f, 387.746f, +153.242f, 387.742f, 154.879f, 386.617f, 135.25f, 390.746f, 135.25f, 390.746f, 128.258f, 393.438f, +126.246f, 404.75f, 126.242f, 404.75f, 121.219f, 409.719f, 116.246f, 415.75f, 110.656f, 422.035f, +138.246f, 415.75f, 138.246f, 415.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 8, 292.25f, 467.746f, 292.25f, 467.746f, 311.848f, 438.297f, 311.246f, +432.75f, 309.758f, 421.598f, 309.539f, 411.035f, 313.246f, 406.75f, 316.578f, 402.238f, 326.25f, +365.746f, 326.25f, 365.746f, 326.25f, 365.742f, 325.82f, 364.398f, 339.25f, 405.746f, 339.25f, +405.742f, 352.219f, 423.797f, 330.25f, 443.75f, 330.25f, 443.75f, 291.5f, 475.719f, 292.25f, +467.746f, 10, 0, 0, 0, 0, 0, 0, 0, 15, +160.246f, 385.746f, 160.246f, 385.742f, 172.699f, 378.035f, 157.25f, 343.746f, 164.246f, 346.746f, +164.242f, 346.746f, 163.02f, 334.035f, 159.246f, 331.75f, 167.25f, 334.75f, 167.25f, 334.75f, +172.699f, 326.117f, 168.25f, 320.75f, 168.25f, 320.75f, 186.777f, 312.035f, 186.25f, 304.746f, +186.25f, 304.746f, 192.938f, 313.797f, 188.246f, 320.75f, 184.137f, 327.879f, 176.219f, 323.477f, +177.246f, 343.746f, 167.25f, 339.746f, 167.25f, 339.742f, 173.578f, 349.879f, 173.25f, 356.75f, +165.246f, 354.746f, 165.242f, 354.742f, 181.793f, 383.512f, 170.246f, 384.75f, 163.457f, 385.957f, +160.246f, 385.746f, 160.246f, 385.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 5, 196.25f, 367.75f, 196.25f, 367.75f, 199.098f, 372.316f, 196.25f, +371.75f, 192.938f, 370.559f, 158.617f, 354.277f, 152.25f, 343.746f, 152.25f, 343.742f, 189.859f, +370.559f, 196.25f, 367.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 5, 207.25f, 358.75f, 207.25f, 358.75f, 210.539f, 363.516f, 207.25f, 362.75f, +204.379f, 361.758f, 170.059f, 345.477f, 163.25f, 334.75f, 163.25f, 334.75f, 201.297f, 361.758f, +207.25f, 358.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +5, 222.246f, 375.75f, 222.246f, 375.75f, 225.059f, 380.238f, 222.246f, 379.746f, 218.898f, +378.477f, 184.578f, 362.195f, 178.246f, 351.75f, 178.246f, 351.75f, 215.816f, 378.477f, 222.246f, +375.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, +196.25f, 327.75f, 196.25f, 327.75f, 196.457f, 334.035f, 193.25f, 332.746f, 190.297f, 332.277f, +150.699f, 312.918f, 144.246f, 302.746f, 144.246f, 302.746f, 190.297f, 330.516f, 196.25f, 327.75f, +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 198.246f, +339.746f, 198.246f, 339.742f, 199.098f, 344.598f, 196.25f, 343.746f, 193.816f, 343.719f, 164.777f, +330.957f, 158.25f, 320.75f, 158.25f, 320.75f, 190.738f, 344.156f, 198.246f, 339.746f, 10, +0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 24, 182.25f, 286.746f, +171.246f, 278.75f, 171.246f, 278.75f, 182.379f, 286.957f, 186.25f, 285.75f, 186.25f, 285.75f, +178.859f, 273.316f, 178.246f, 267.75f, 178.246f, 267.75f, 189.418f, 281.676f, 195.25f, 280.746f, +195.25f, 280.746f, 203.938f, 280.797f, 204.25f, 268.75f, 204.25f, 268.75f, 210.098f, 280.355f, +213.246f, 279.75f, 213.242f, 279.75f, 214.938f, 272.879f, 213.246f, 265.75f, 213.242f, 265.75f, +218.02f, 273.758f, 222.246f, 271.746f, 222.246f, 271.746f, 229.457f, 274.195f, 228.25f, 261.746f, +228.25f, 261.742f, 228.578f, 249.996f, 227.25f, 246.746f, 227.25f, 246.746f, 233.859f, 275.957f, +236.25f, 276.75f, 236.25f, 276.75f, 245.297f, 277.719f, 250.25f, 267.75f, 250.25f, 267.75f, +246.18f, 276.398f, 251.25f, 273.746f, 251.25f, 273.742f, 263.34f, 272.438f, 267.246f, 264.746f, +267.246f, 264.742f, 259.379f, 278.156f, 265.246f, 274.75f, 265.246f, 274.75f, 273.02f, 274.637f, +274.25f, 267.75f, 274.25f, 267.75f, 283.578f, 244.277f, 286.246f, 242.75f, 286.246f, 242.75f, +277.418f, 266.277f, 279.246f, 266.746f, 279.242f, 266.742f, 276.977f, 279.477f, 282.25f, 262.75f, +282.25f, 262.75f, 279.18f, 278.598f, 285.25f, 277.746f, 291.5f, 276.836f, 296.34f, 265.836f, +305.25f, 268.75f, 305.25f, 268.75f, 316.141f, 262.316f, 318.25f, 338.75f, 182.25f, 286.746f, +10, 0, 0, 0, 0, 0, 0, 0, 15, 187.246f, +388.75f, 187.246f, 388.75f, 203.5f, 395.637f, 247.246f, 388.75f, 247.242f, 388.75f, 255.418f, +388.598f, 263.25f, 398.746f, 270.379f, 407.957f, 299.859f, 415.879f, 307.25f, 413.75f, 317.25f, +406.75f, 318.25f, 405.746f, 318.25f, 405.742f, 331.98f, 393.879f, 332.246f, 385.746f, 332.859f, +377.156f, 316.578f, 324.355f, 306.25f, 306.746f, 295.457f, 289.156f, 284.898f, 275.516f, 264.246f, +277.746f, 264.246f, 277.742f, 240.898f, 282.559f, 212.246f, 277.746f, 212.246f, 277.742f, 180.617f, +279.918f, 177.246f, 288.746f, 174.457f, 297.516f, 190.246f, 313.746f, 190.246f, 313.746f, 190.246f, +313.746f, 194.699f, 323.477f, 193.25f, 339.746f, 192.059f, 355.156f, 192.5f, 385.957f, 187.246f, +388.75f, 10, 0, 0.9f, 0.4f, 0.55f, 0.9f, 0.4f, 0.55f, 8, +211.246f, 386.75f, 220.656f, 366.598f, 188.246f, 294.75f, 188.246f, 294.75f, 185.898f, 293.117f, +202.023f, 286.469f, 213.246f, 288.746f, 225.219f, 292.059f, 269.246f, 287.75f, 269.246f, 287.75f, +295.457f, 304.559f, 309.246f, 353.75f, 309.246f, 353.75f, 309.246f, 353.75f, 320.98f, 379.797f, +301.246f, 383.746f, 282.258f, 386.836f, 211.246f, 386.75f, 211.246f, 386.75f, 10, 0, +0.7f, 0.2f, 0.35f, 0.7f, 0.2f, 0.35f, 6, 209.246f, 352.746f, 212.844f, +366.922f, 214.586f, 379.902f, 211.246f, 386.75f, 211.246f, 386.75f, 280.059f, 379.797f, 292.25f, +402.75f, 297.043f, 411.34f, 313.277f, 377.598f, 313.246f, 366.75f, 313.242f, 366.75f, 243.539f, +351.195f, 227.25f, 363.746f, 209.246f, 352.746f, 10, 0, 0.65f, 0.15f, 0.3f, +0.65f, 0.15f, 0.3f, 13, 214.25f, 334.75f, 214.25f, 334.75f, 216.258f, 326.996f, +213.246f, 322.75f, 213.242f, 322.75f, 211.859f, 321.719f, 210.246f, 321.746f, 210.246f, 321.742f, +211.859f, 317.316f, 218.25f, 315.746f, 218.25f, 315.746f, 220.656f, 310.719f, 223.246f, 310.746f, +225.938f, 309.836f, 231.219f, 303.676f, 235.246f, 304.746f, 240.02f, 306.316f, 252.25f, 310.746f, +252.25f, 310.746f, 252.25f, 310.742f, 258.5f, 314.238f, 268.246f, 310.746f, 268.242f, 310.742f, +270.789f, 311.16f, 271.25f, 315.746f, 271.809f, 320.727f, 275.219f, 324.797f, 277.246f, 326.746f, +279.617f, 329.195f, 290.18f, 343.277f, 289.246f, 343.746f, 287.539f, 344.156f, 214.25f, 334.75f, +214.25f, 334.75f, 10, 0, 1, 0.45f, 0.5f, 1, 0.45f, 0.5f, +12, 209.246f, 387.746f, 209.246f, 387.742f, 206.137f, 363.516f, 209.246f, 354.746f, 213.18f, +345.035f, 212.297f, 342.836f, 211.246f, 338.75f, 210.539f, 334.035f, 215.379f, 323.035f, 221.246f, +316.75f, 234.246f, 314.75f, 234.246f, 314.75f, 251.457f, 318.637f, 261.25f, 315.746f, 261.25f, +315.746f, 271.473f, 314.078f, 275.246f, 330.746f, 275.246f, 330.742f, 280.5f, 337.559f, 288.246f, +340.75f, 296.34f, 343.719f, 304.258f, 389.477f, 300.246f, 398.746f, 295.457f, 407.078f, 279.617f, +411.918f, 262.25f, 394.746f, 244.418f, 377.598f, 242.219f, 396.078f, 209.246f, 387.746f, 10, +1.1f, 0, 0, 0, 0, 0, 0, 12, 209.246f, 387.746f, +209.246f, 387.742f, 206.137f, 363.516f, 209.246f, 354.746f, 213.18f, 345.035f, 212.297f, 342.836f, +211.246f, 338.75f, 210.539f, 334.035f, 215.379f, 323.035f, 221.246f, 316.75f, 234.246f, 314.75f, +234.246f, 314.75f, 251.457f, 318.637f, 261.25f, 315.746f, 261.25f, 315.746f, 271.473f, 314.078f, +275.246f, 330.746f, 275.246f, 330.742f, 280.5f, 337.559f, 288.246f, 340.75f, 296.34f, 343.719f, +304.258f, 389.477f, 300.246f, 398.746f, 295.457f, 407.078f, 279.617f, 411.918f, 262.25f, 394.746f, +244.418f, 377.598f, 242.219f, 396.078f, 209.246f, 387.746f, 10, 0, 1, 1, +0.8f, 1, 1, 0.8f, 7, 211.246f, 305.75f, 211.246f, 305.75f, 210.098f, +308.078f, 205.25f, 308.746f, 205.25f, 308.742f, 180.617f, 312.477f, 171.246f, 325.75f, 171.246f, +325.75f, 163.898f, 332.277f, 168.25f, 319.746f, 168.25f, 319.742f, 180.18f, 297.078f, 187.246f, +293.746f, 187.246f, 293.746f, 205.699f, 289.598f, 211.246f, 305.75f, 10, 0.55f, 0, +0, 0, 0, 0, 0, 7, 211.246f, 305.75f, 211.246f, 305.75f, +210.098f, 308.078f, 205.25f, 308.746f, 205.25f, 308.742f, 180.617f, 312.477f, 171.246f, 325.75f, +171.246f, 325.75f, 163.898f, 332.277f, 168.25f, 319.746f, 168.25f, 319.742f, 180.18f, 297.078f, +187.246f, 293.746f, 187.246f, 293.746f, 205.699f, 289.598f, 211.246f, 305.75f, 10, 0, +0.8f, 0.25f, 0.3f, 0.8f, 0.25f, 0.3f, 9, 299.246f, 375.75f, 299.641f, +384.941f, 301.789f, 394.418f, 300.246f, 398.746f, 292.766f, 412.461f, 274.098f, 406.535f, 262.25f, +394.746f, 244.418f, 377.598f, 242.219f, 396.078f, 209.246f, 387.746f, 209.246f, 387.742f, 207.297f, +372.797f, 208.25f, 361.746f, 208.25f, 361.742f, 249.258f, 374.516f, 250.25f, 368.746f, 250.25f, +368.746f, 251.898f, 371.879f, 262.25f, 371.75f, 272.137f, 371.879f, 297.152f, 373.168f, 299.246f, +375.75f, 10, 2.2f, 0.65f, 0.1f, 0.15f, 0.65f, 0.1f, 0.15f, 3, +251.25f, 387.746f, 251.25f, 387.742f, 256.738f, 381.996f, 253.246f, 371.75f, 253.246f, 371.75f, +236.938f, 353.836f, 239.25f, 338.75f, 10, 0, 1, 1, 0.8f, 1, +1, 0.8f, 5, 198.246f, 293.746f, 198.246f, 293.746f, 193.816f, 308.078f, 203.25f, +300.75f, 203.25f, 300.75f, 208.777f, 298.398f, 207.25f, 296.75f, 206.137f, 294.879f, 199.977f, +290.477f, 198.246f, 293.746f, 10, 0.55f, 0, 0, 0, 0, 0, +0, 5, 198.246f, 293.746f, 198.246f, 293.746f, 193.816f, 308.078f, 203.25f, 300.75f, +203.25f, 300.75f, 208.777f, 298.398f, 207.25f, 296.75f, 206.137f, 294.879f, 199.977f, 290.477f, +198.246f, 293.746f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, +5, 204.25f, 292.75f, 204.25f, 292.75f, 200.328f, 303.941f, 208.25f, 297.746f, 208.25f, +297.742f, 212.937f, 295.266f, 211.246f, 294.75f, 206.227f, 293.383f, 211.242f, 290.566f, 204.25f, +292.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, +204.25f, 292.75f, 204.25f, 292.75f, 200.328f, 303.941f, 208.25f, 297.746f, 208.25f, 297.742f, +212.937f, 295.266f, 211.246f, 294.75f, 206.227f, 293.383f, 211.242f, 290.566f, 204.25f, 292.75f, +10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 209.246f, +292.75f, 209.246f, 292.75f, 205.609f, 303.941f, 213.246f, 297.746f, 213.242f, 297.742f, 218.168f, +295.414f, 216.25f, 294.75f, 212.824f, 293.383f, 216.523f, 290.566f, 209.246f, 292.75f, 10, +0.55f, 0, 0, 0, 0, 0, 0, 5, 209.246f, 292.75f, +209.246f, 292.75f, 205.609f, 303.941f, 213.246f, 297.746f, 213.242f, 297.742f, 218.168f, 295.414f, +216.25f, 294.75f, 212.824f, 293.383f, 216.523f, 290.566f, 209.246f, 292.75f, 10, 0, +1, 1, 0.8f, 1, 1, 0.8f, 5, 216.25f, 292.75f, 216.25f, +292.75f, 212.871f, 303.723f, 220.246f, 297.746f, 220.246f, 297.742f, 225.434f, 295.172f, 224.246f, +294.75f, 220.527f, 293.383f, 223.781f, 290.344f, 216.25f, 292.75f, 10, 0.55f, 0, +0, 0, 0, 0, 0, 5, 216.25f, 292.75f, 216.25f, 292.75f, +212.871f, 303.723f, 220.246f, 297.746f, 220.246f, 297.742f, 225.434f, 295.172f, 224.246f, 294.75f, +220.527f, 293.383f, 223.781f, 290.344f, 216.25f, 292.75f, 10, 0, 1, 1, +0.8f, 1, 1, 0.8f, 5, 224.246f, 292.75f, 224.242f, 292.75f, 220, +303.809f, 227.25f, 297.746f, 227.25f, 297.742f, 231.969f, 296.066f, 231.246f, 294.75f, 229.855f, +293.25f, 230.91f, 290.434f, 224.246f, 292.75f, 10, 0.55f, 0, 0, 0, +0, 0, 0, 5, 224.246f, 292.75f, 224.242f, 292.75f, 220, 303.809f, +227.25f, 297.746f, 227.25f, 297.742f, 231.969f, 296.066f, 231.246f, 294.75f, 229.855f, 293.25f, +230.91f, 290.434f, 224.246f, 292.75f, 10, 0, 1, 1, 0.8f, 1, +1, 0.8f, 5, 231.246f, 291.746f, 231.246f, 291.746f, 225.938f, 305.438f, 236.25f, +298.75f, 236.25f, 298.75f, 241.34f, 296.195f, 240.25f, 294.75f, 238.699f, 292.676f, 240.02f, +289.156f, 231.246f, 291.746f, 10, 0.55f, 0, 0, 0, 0, 0, +0, 5, 231.246f, 291.746f, 231.246f, 291.746f, 225.938f, 305.438f, 236.25f, 298.75f, +236.25f, 298.75f, 241.34f, 296.195f, 240.25f, 294.75f, 238.699f, 292.676f, 240.02f, 289.156f, +231.246f, 291.746f, 10, 2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, +4, 200.246f, 310.746f, 200.246f, 310.742f, 214.5f, 313.797f, 221.246f, 310.746f, 221.246f, +310.742f, 227.699f, 308.957f, 229.25f, 309.75f, 230.34f, 309.836f, 234.246f, 310.746f, 234.246f, +310.746f, 10, 2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, 4, +237.25f, 300.75f, 237.25f, 300.75f, 250.578f, 315.996f, 264.246f, 310.746f, 271.496f, 308.328f, +270.379f, 312.035f, 271.25f, 314.75f, 272.137f, 318.195f, 272.359f, 322.816f, 278.246f, 325.75f, +10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 7, 256.246f, +318.75f, 256.246f, 318.75f, 251.898f, 330.516f, 249.25f, 316.75f, 245.738f, 302.355f, 242.219f, +298.398f, 240.25f, 295.746f, 240.25f, 295.742f, 240.457f, 289.598f, 249.25f, 289.75f, 249.25f, +289.75f, 261.578f, 290.477f, 262.25f, 293.746f, 262.457f, 296.637f, 260.699f, 309.398f, 256.246f, +318.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 7, +256.246f, 318.75f, 256.246f, 318.75f, 251.898f, 330.516f, 249.25f, 316.75f, 245.738f, 302.355f, +242.219f, 298.398f, 240.25f, 295.746f, 240.25f, 295.742f, 240.457f, 289.598f, 249.25f, 289.75f, +249.25f, 289.75f, 261.578f, 290.477f, 262.25f, 293.746f, 262.457f, 296.637f, 260.699f, 309.398f, +256.246f, 318.75f, 10, 2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, +2, 271.25f, 310.746f, 271.25f, 310.742f, 275.656f, 313.355f, 278.246f, 311.75f, 10, +2.2f, 0.65f, 0.15f, 0.3f, 0.65f, 0.15f, 0.3f, 2, 279.246f, 328.746f, +279.242f, 328.742f, 282.039f, 334.148f, 287.246f, 334.75f, 10, 0, 0.7f, 0.7f, +0.7f, 0.7f, 0.7f, 0.7f, 6, 191.246f, 288.746f, 191.242f, 288.742f, 211.418f, +284.758f, 216.25f, 286.746f, 216.25f, 286.742f, 225.938f, 286.516f, 216.25f, 284.746f, 216.25f, +284.742f, 202.617f, 284.316f, 194.25f, 285.75f, 194.25f, 285.75f, 181.059f, 291.797f, 191.246f, +288.746f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 7, +207.25f, 390.746f, 207.25f, 390.746f, 226.379f, 390.797f, 228.25f, 389.75f, 228.25f, 389.75f, +236.5f, 356.035f, 232.246f, 347.75f, 232.246f, 347.75f, 231.219f, 344.598f, 228.25f, 350.746f, +228.25f, 350.742f, 207.898f, 386.836f, 204.25f, 388.75f, 200.859f, 391.238f, 205.699f, 390.797f, +207.25f, 390.746f, 10, 0.55f, 0, 0, 0, 0, 0, 0, +7, 207.25f, 390.746f, 207.25f, 390.746f, 226.379f, 390.797f, 228.25f, 389.75f, 228.25f, +389.75f, 236.5f, 356.035f, 232.246f, 347.75f, 232.246f, 347.75f, 231.219f, 344.598f, 228.25f, +350.746f, 228.25f, 350.742f, 207.898f, 386.836f, 204.25f, 388.75f, 200.859f, 391.238f, 205.699f, +390.797f, 207.25f, 390.746f, 10, 0, 1, 1, 0.8f, 1, 1, +0.8f, 7, 122.246f, 393.75f, 122.246f, 393.75f, 132, 391.898f, 146.25f, 388.75f, +146.25f, 388.75f, 151.137f, 364.398f, 154.246f, 358.75f, 158.18f, 353.836f, 154.219f, 353.836f, +150.246f, 356.75f, 146.297f, 359.996f, 130.02f, 375.398f, 128.25f, 379.746f, 125.617f, 385.078f, +122.246f, 393.75f, 122.246f, 393.75f, 10, 0.55f, 0, 0, 0, 0, +0, 0, 7, 122.246f, 393.75f, 122.246f, 393.75f, 132, 391.898f, 146.25f, +388.75f, 146.25f, 388.75f, 151.137f, 364.398f, 154.246f, 358.75f, 158.18f, 353.836f, 154.219f, +353.836f, 150.246f, 356.75f, 146.297f, 359.996f, 130.02f, 375.398f, 128.25f, 379.746f, 125.617f, +385.078f, 122.246f, 393.75f, 122.246f, 393.75f, 10, 0, 1, 1, 0.8f, +1, 1, 0.8f, 6, 146.25f, 388.75f, 146.25f, 388.75f, 152.637f, 387.094f, +153.246f, 384.75f, 154.855f, 382.223f, 152.25f, 378.75f, 152.25f, 378.75f, 152.25f, 378.75f, +151.324f, 374.961f, 150.246f, 377.75f, 148.68f, 379.719f, 145.52f, 388.145f, 146.25f, 388.75f, +10, 0.55f, 0, 0, 0, 0, 0, 0, 6, 146.25f, +388.75f, 146.25f, 388.75f, 152.637f, 387.094f, 153.246f, 384.75f, 154.855f, 382.223f, 152.25f, +378.75f, 152.25f, 378.75f, 152.25f, 378.75f, 151.324f, 374.961f, 150.246f, 377.75f, 148.68f, +379.719f, 145.52f, 388.145f, 146.25f, 388.75f, 10, 0, 0, 0, 0, +0, 0, 0, 10, 146.25f, 388.75f, 146.25f, 388.75f, 150.258f, 383.316f, +154.246f, 383.746f, 158.18f, 383.316f, 158.598f, 383.77f, 161.246f, 382.75f, 166.758f, 381.996f, +166.316f, 384.195f, 173.25f, 382.75f, 176.48f, 382.348f, 179.297f, 383.316f, 182.25f, 381.746f, +185.457f, 380.676f, 188.977f, 381.559f, 190.246f, 383.746f, 191.617f, 385.957f, 197.25f, 390.746f, +197.25f, 390.746f, 197.25f, 390.746f, 182.816f, 388.598f, 179.246f, 387.746f, 179.246f, 387.742f, +155.098f, 386.398f, 146.25f, 388.75f, 10, 0, 1, 1, 0.8f, 1, +1, 0.8f, 6, 195.25f, 388.75f, 195.25f, 388.75f, 188.262f, 384.969f, 188.246f, +382.75f, 187.383f, 379.688f, 193.25f, 375.75f, 193.25f, 375.75f, 193.25f, 375.75f, 196.625f, +370.559f, 197.25f, 372.746f, 197.941f, 375.836f, 196.238f, 388.379f, 195.25f, 388.75f, 10, +0.55f, 0, 0, 0, 0, 0, 0, 6, 195.25f, 388.75f, +195.25f, 388.75f, 188.262f, 384.969f, 188.246f, 382.75f, 187.383f, 379.688f, 193.25f, 375.75f, +193.25f, 375.75f, 193.25f, 375.75f, 196.625f, 370.559f, 197.25f, 372.746f, 197.941f, 375.836f, +196.238f, 388.379f, 195.25f, 388.75f, 10, 0, 1, 1, 0.8f, 1, +1, 0.8f, 5, 154.246f, 382.75f, 154.242f, 382.75f, 161.832f, 370.5f, 162.25f, +382.75f, 162.25f, 382.75f, 162.684f, 384.215f, 160.246f, 383.746f, 154.066f, 384.324f, 155.738f, +388.836f, 154.246f, 382.75f, 10, 0.55f, 0, 0, 0, 0, 0, +0, 5, 154.246f, 382.75f, 154.242f, 382.75f, 161.832f, 370.5f, 162.25f, 382.75f, +162.25f, 382.75f, 162.684f, 384.215f, 160.246f, 383.746f, 154.066f, 384.324f, 155.738f, 388.836f, +154.246f, 382.75f, 10, 0, 1, 1, 0.8f, 1, 1, 0.8f, +5, 162.25f, 382.75f, 162.25f, 382.75f, 170.734f, 370.227f, 170.246f, 382.75f, 170.242f, +382.75f, 170.043f, 383, 168.25f, 382.75f, 162.891f, 383.625f, 163.27f, 388.594f, 162.25f, +382.75f, 10, 0.55f, 0, 0, 0, 0, 0, 0, 5, +162.25f, 382.75f, 162.25f, 382.75f, 170.734f, 370.227f, 170.246f, 382.75f, 170.242f, 382.75f, +170.043f, 383, 168.25f, 382.75f, 162.891f, 383.625f, 163.27f, 388.594f, 162.25f, 382.75f, +10, 0, 1, 1, 0.8f, 1, 1, 0.8f, 5, 170.246f, +382.75f, 170.242f, 382.75f, 178.711f, 370.832f, 178.246f, 381.746f, 178.246f, 381.746f, 178.105f, +382.82f, 176.246f, 382.75f, 172.004f, 383.93f, 171.773f, 387.504f, 170.246f, 382.75f, 10, +0.55f, 0, 0, 0, 0, 0, 0, 5, 170.246f, 382.75f, +170.242f, 382.75f, 178.711f, 370.832f, 178.246f, 381.746f, 178.246f, 381.746f, 178.105f, 382.82f, +176.246f, 382.75f, 172.004f, 383.93f, 171.773f, 387.504f, 170.246f, 382.75f, 10, 0, +1, 1, 0.8f, 1, 1, 0.8f, 5, 177.246f, 382.75f, 177.246f, +382.75f, 186.207f, 369.719f, 186.25f, 380.75f, 186.25f, 380.75f, 188.398f, 381.992f, 186.25f, +381.746f, 180.078f, 383.051f, 180.957f, 387.953f, 177.246f, 382.75f, 10, 0.55f, 0, +0, 0, 0, 0, 0, 5, 177.246f, 382.75f, 177.246f, 382.75f, +186.207f, 369.719f, 186.25f, 380.75f, 186.25f, 380.75f, 188.398f, 381.992f, 186.25f, 381.746f, +180.078f, 383.051f, 180.957f, 387.953f, 177.246f, 382.75f, 10, 0, 0.9f, 0.9f, +0.7f, 0.9f, 0.9f, 0.7f, 6, 137.246f, 378.75f, 129.25f, 379.746f, 126.441f, +385.738f, 124.25f, 392.746f, 124.25f, 392.746f, 124.25f, 392.746f, 131.117f, 391.402f, 145.25f, +388.75f, 145.25f, 388.75f, 145.832f, 384.672f, 147.25f, 378.75f, 137.246f, 378.75f, 10, +0, 0.9f, 0.9f, 0.7f, 0.9f, 0.9f, 0.7f, 7, 209.246f, 383.746f, +207.469f, 386.437f, 206.02f, 388.371f, 205.25f, 388.75f, 201.992f, 390.891f, 206.547f, 390.477f, +208.25f, 390.746f, 208.25f, 390.746f, 226.02f, 390.477f, 228.25f, 389.75f, 228.25f, 389.75f, +228.668f, 387.18f, 229.25f, 383.746f, 229.25f, 383.742f, 218.32f, 385.66f, 209.246f, 383.746f, +10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 10, 268.246f, +535.746f, 298.758f, 531.289f, 326.832f, 570.492f, 329.25f, 580.75f, 330.703f, 591.789f, 319.25f, +604.75f, 319.25f, 604.75f, 321.023f, 608.246f, 315.699f, 623.734f, 310.246f, 633.75f, 304.082f, +644.063f, 286.594f, 642.992f, 267.246f, 643.75f, 249.875f, 645.031f, 229.547f, 619.379f, 228.25f, +617.75f, 226.641f, 615.508f, 233.418f, 573.398f, 235.246f, 566.75f, 236.32f, 560.813f, 233.246f, +531.75f, 233.246f, 531.75f, 271.082f, 541.781f, 237.773f, 540, 268.246f, 535.746f, 10, +0, 0.92f, 0.56f, 0.32f, 0.92f, 0.56f, 0.32f, 10, 229.25f, 616.746f, +227.469f, 614.828f, 234.121f, 573.484f, 235.246f, 567.75f, 236.973f, 561.129f, 234.246f, 532.746f, +234.246f, 532.746f, 270.063f, 542.387f, 238.398f, 540.695f, 268.246f, 536.75f, 298.273f, 532.141f, +325.836f, 570.633f, 327.25f, 580.75f, 329.637f, 591.543f, 318.25f, 604.75f, 318.25f, 604.75f, +320.133f, 607.699f, 314.906f, 622.906f, 309.246f, 632.75f, 303.504f, 642.867f, 286.332f, 641.813f, +267.246f, 642.746f, 250.277f, 643.816f, 230.32f, 618.629f, 229.25f, 616.746f, 10, 0, +0.94f, 0.67f, 0.49f, 0.94f, 0.67f, 0.49f, 10, 229.25f, 615.75f, 228.297f, +614.152f, 234.824f, 573.574f, 236.25f, 567.75f, 237.625f, 561.445f, 235.246f, 533.746f, 235.246f, +533.746f, 269.371f, 543.539f, 239.023f, 541.391f, 268.246f, 536.75f, 297.793f, 532.996f, 324.844f, +570.773f, 326.25f, 580.75f, 328.574f, 591.297f, 318.25f, 603.746f, 318.25f, 603.746f, 319.246f, +607.156f, 314.113f, 622.078f, 308.246f, 631.746f, 302.922f, 641.668f, 286.066f, 640.637f, 267.246f, +641.75f, 250.684f, 642.602f, 231.094f, 617.883f, 229.25f, 615.75f, 10, 0, 0.96f, +0.78f, 0.66f, 0.96f, 0.78f, 0.66f, 10, 230.25f, 615.75f, 229.125f, 613.473f, +235.531f, 573.66f, 237.25f, 567.75f, 238.277f, 561.762f, 235.246f, 534.75f, 235.246f, 534.75f, +267.91f, 544.25f, 239.648f, 542.086f, 268.246f, 537.746f, 297.309f, 533.852f, 323.848f, 570.914f, +325.25f, 580.75f, 327.508f, 591.051f, 317.25f, 603.746f, 317.25f, 603.746f, 318.355f, 606.609f, +313.324f, 621.254f, 308.246f, 630.75f, 302.34f, 640.473f, 285.805f, 639.457f, 267.246f, 640.746f, +251.09f, 641.387f, 231.871f, 617.133f, 230.25f, 615.75f, 10, 0, 0.98f, 0.89f, +0.83f, 0.98f, 0.89f, 0.83f, 10, 231.246f, 614.746f, 229.949f, 612.797f, 236.234f, +573.75f, 237.25f, 567.75f, 238.926f, 562.082f, 236.25f, 534.75f, 236.25f, 534.75f, 266.891f, +544.965f, 240.273f, 542.781f, 268.246f, 538.75f, 296.824f, 534.703f, 322.855f, 571.055f, 324.246f, +580.75f, 326.445f, 590.805f, 316.25f, 602.75f, 316.25f, 602.75f, 317.469f, 606.063f, 312.531f, +620.426f, 307.25f, 629.746f, 301.762f, 639.273f, 285.543f, 638.281f, 267.246f, 639.75f, 251.492f, +640.172f, 232.645f, 616.387f, 231.246f, 614.746f, 10, 0, 1, 1, 1, +1, 1, 1, 10, 268.246f, 539.746f, 296.34f, 535.559f, 321.859f, 571.199f, +323.246f, 580.75f, 325.379f, 590.559f, 315.25f, 602.75f, 315.25f, 602.75f, 316.578f, 605.52f, +311.738f, 619.598f, 306.25f, 628.75f, 301.18f, 638.078f, 285.277f, 637.102f, 267.246f, 637.75f, +251.898f, 638.957f, 233.418f, 615.637f, 232.246f, 613.75f, 230.777f, 612.117f, 236.938f, 573.84f, +238.25f, 567.75f, 239.578f, 562.398f, 237.25f, 535.746f, 237.25f, 535.746f, 264.988f, 545.457f, +240.898f, 543.477f, 268.246f, 539.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 6, 319.25f, 576.746f, 319.25f, 576.742f, 289.078f, 568.559f, 276.246f, +570.746f, 276.246f, 570.746f, 258.937f, 577.578f, 249.25f, 553.75f, 249.25f, 553.75f, 245.297f, +545.68f, 243.246f, 543.746f, 240.898f, 541.277f, 319.25f, 576.746f, 319.25f, 576.746f, 10, +0, 0, 0, 0, 0, 0, 0, 11, 324.246f, 579.746f, +324.242f, 579.746f, 291.937f, 565.918f, 281.25f, 566.75f, 281.25f, 566.75f, 262.898f, 571.418f, +253.246f, 555.75f, 253.246f, 555.75f, 244.418f, 545.238f, 241.25f, 543.746f, 241.25f, 543.742f, +240.457f, 541.719f, 247.246f, 545.75f, 259.25f, 540.75f, 259.25f, 540.75f, 275.219f, 529.84f, +286.246f, 547.75f, 286.246f, 547.75f, 290.18f, 559.758f, 290.246f, 561.746f, 290.18f, 564.156f, +313.5f, 570.316f, 315.25f, 570.746f, 317.02f, 571.199f, 324.277f, 575.816f, 324.246f, 579.746f, +10, 0, 0.6f, 0.8f, 0.2f, 0.6f, 0.8f, 0.2f, 6, 271.25f, +539.746f, 264.141f, 539.832f, 254.93f, 544.086f, 255.246f, 550.746f, 254.93f, 557.832f, 264.141f, +564.723f, 271.25f, 564.75f, 279.258f, 564.723f, 285.387f, 559.152f, 285.25f, 552.746f, 285.387f, +545.402f, 279.258f, 539.832f, 271.25f, 539.746f, 10, 0, 0.4f, 0.6f, 0, +0.4f, 0.6f, 0, 6, 267.246f, 557.746f, 262.383f, 557.391f, 256.785f, 555.738f, +257.246f, 555.75f, 258.559f, 561.055f, 265.555f, 564.723f, 271.25f, 564.75f, 276.422f, 564.723f, +280.59f, 562.547f, 283.25f, 558.75f, 283.25f, 558.75f, 277.203f, 559.598f, 267.246f, 557.746f, +10, 0, 1, 1, 1, 1, 1, 1, 4, 281.25f, +558.75f, 281.25f, 558.75f, 276.098f, 561.957f, 276.246f, 559.746f, 276.246f, 559.746f, 280.059f, +554.699f, 281.25f, 558.75f, 10, 0, 0, 0, 0, 0, 0, +0, 6, 270.25f, 549.75f, 267.187f, 549.5f, 264.961f, 551.727f, 265.246f, 554.746f, +264.961f, 557.227f, 267.187f, 559.457f, 270.25f, 559.746f, 272.687f, 559.457f, 274.918f, 557.227f, +275.246f, 554.746f, 274.918f, 551.727f, 272.687f, 549.5f, 270.25f, 549.75f, 10, 0, +0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 15, 155.246f, 563.746f, 155.246f, +563.742f, 152.02f, 587.477f, 154.246f, 592.746f, 154.242f, 592.746f, 166.539f, 603.316f, 166.246f, +607.746f, 166.246f, 607.742f, 165.656f, 627.078f, 164.246f, 627.746f, 163.02f, 628.84f, 154.656f, +635.438f, 148.246f, 628.75f, 148.242f, 628.75f, 136.617f, 608.598f, 137.246f, 601.746f, 137.246f, +599.75f, 137.242f, 599.75f, 129.137f, 599.797f, 127.246f, 597.75f, 127.246f, 597.75f, 126.059f, +591.879f, 124.25f, 591.75f, 124.25f, 591.75f, 121.656f, 588.797f, 124.25f, 585.746f, 124.25f, +585.742f, 121.656f, 583.078f, 122.246f, 578.75f, 130.25f, 574.746f, 130.25f, 574.742f, 132.656f, +558.438f, 144.246f, 552.746f, 149.859f, 550.156f, 153.34f, 557.559f, 155.246f, 563.746f, 10, +0, 1, 1, 1, 1, 1, 1, 15, 154.246f, 565.746f, +154.242f, 565.742f, 151.27f, 587.172f, 153.246f, 591.75f, 153.242f, 591.75f, 164.34f, 601.426f, +164.246f, 604.75f, 164.242f, 604.75f, 163.547f, 622.809f, 162.25f, 623.746f, 161.172f, 624.395f, +153.645f, 630.336f, 147.25f, 623.746f, 147.25f, 623.746f, 137.41f, 606.18f, 138.246f, 599.75f, +138.246f, 597.75f, 138.246f, 597.75f, 130.68f, 598.258f, 129.25f, 596.746f, 129.25f, 596.742f, +127.906f, 591.129f, 126.246f, 590.746f, 126.242f, 590.746f, 123.945f, 588.359f, 126.246f, 585.746f, +126.242f, 585.742f, 123.945f, 583.211f, 124.25f, 579.746f, 132.246f, 575.75f, 132.246f, 575.75f, +133.848f, 561.035f, 144.246f, 555.75f, 149.324f, 553.582f, 152.457f, 560.242f, 154.246f, 565.746f, +10, 0, 0.925f, 0.588f, 0.363f, 0.925f, 0.588f, 0.363f, 15, 164.246f, +626.75f, 162.645f, 627.816f, 154.406f, 634.16f, 148.246f, 627.746f, 148.242f, 627.742f, 136.816f, +607.992f, 137.246f, 600.75f, 137.246f, 598.746f, 137.242f, 598.742f, 129.523f, 599.414f, 128.25f, +597.75f, 128.25f, 597.75f, 126.52f, 591.691f, 125.25f, 591.75f, 125.25f, 591.75f, 122.23f, +588.688f, 124.25f, 585.746f, 124.25f, 585.742f, 122.23f, 583.109f, 122.246f, 578.75f, 131.246f, +574.746f, 131.242f, 574.742f, 132.953f, 559.086f, 144.246f, 553.75f, 149.723f, 551.012f, 153.117f, +558.23f, 155.246f, 564.75f, 155.246f, 564.75f, 151.832f, 587.402f, 154.246f, 591.75f, 154.242f, +591.75f, 165.988f, 602.844f, 165.246f, 606.75f, 165.242f, 606.75f, 165.129f, 626.012f, 164.246f, +626.75f, 10, 0, 0.95f, 0.725f, 0.575f, 0.95f, 0.725f, 0.575f, 15, +163.25f, 625.746f, 162.27f, 626.793f, 154.152f, 632.887f, 148.246f, 625.746f, 148.242f, 625.746f, +137.016f, 607.387f, 138.246f, 600.75f, 138.246f, 598.746f, 138.246f, 598.742f, 129.906f, 599.027f, +128.25f, 596.746f, 128.25f, 596.742f, 126.98f, 591.504f, 125.25f, 590.746f, 125.25f, 590.746f, +122.801f, 588.578f, 125.25f, 585.746f, 125.25f, 585.742f, 122.801f, 583.145f, 123.25f, 578.75f, +131.246f, 574.746f, 131.242f, 574.742f, 133.254f, 559.734f, 144.246f, 554.746f, 149.59f, 551.867f, +152.898f, 558.898f, 155.246f, 564.75f, 155.246f, 564.75f, 151.645f, 587.324f, 154.246f, 591.75f, +154.242f, 591.75f, 165.438f, 602.371f, 165.246f, 605.746f, 165.242f, 605.742f, 164.602f, 624.945f, +163.25f, 625.746f, 10, 0, 0.975f, 0.863f, 0.788f, 0.975f, 0.863f, 0.788f, +15, 163.25f, 624.75f, 161.895f, 625.77f, 153.898f, 631.609f, 148.246f, 624.75f, 148.242f, +624.75f, 137.211f, 606.781f, 138.246f, 600.75f, 138.246f, 597.75f, 138.246f, 597.75f, 130.293f, +598.645f, 128.25f, 596.746f, 128.25f, 596.742f, 127.445f, 591.316f, 126.246f, 590.746f, 126.242f, +590.746f, 123.375f, 588.469f, 125.25f, 585.746f, 125.25f, 585.742f, 123.375f, 583.176f, 124.25f, +578.75f, 131.246f, 574.746f, 131.242f, 574.742f, 133.551f, 560.387f, 144.246f, 554.746f, 149.457f, +552.727f, 152.68f, 559.57f, 154.246f, 565.746f, 154.242f, 565.742f, 151.457f, 587.246f, 154.246f, +591.75f, 154.242f, 591.75f, 164.887f, 601.898f, 164.246f, 605.746f, 164.242f, 605.742f, 164.074f, +623.879f, 163.25f, 624.75f, 10, 0, 1, 1, 1, 1, 1, +1, 15, 154.246f, 566.75f, 154.242f, 566.75f, 151.27f, 587.172f, 153.246f, 591.75f, +153.242f, 591.75f, 164.34f, 601.426f, 164.246f, 604.75f, 164.242f, 604.75f, 163.547f, 622.809f, +162.25f, 623.746f, 161.523f, 624.746f, 153.645f, 630.336f, 147.25f, 623.746f, 147.25f, 623.746f, +137.41f, 606.18f, 138.246f, 599.75f, 138.246f, 597.75f, 138.246f, 597.75f, 130.68f, 598.258f, +129.25f, 596.746f, 129.25f, 596.742f, 127.906f, 591.129f, 126.246f, 590.746f, 126.242f, 590.746f, +123.945f, 588.359f, 126.246f, 585.746f, 126.242f, 585.742f, 123.945f, 583.211f, 124.25f, 579.746f, +132.246f, 575.75f, 132.246f, 575.75f, 133.848f, 561.035f, 144.246f, 555.75f, 149.324f, 553.582f, +152.457f, 560.352f, 154.246f, 566.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 5, 151.25f, 572.746f, 151.25f, 572.742f, 127.27f, 584.398f, 126.246f, +585.746f, 126.242f, 585.742f, 136.289f, 576.258f, 137.246f, 576.746f, 138.047f, 576.258f, 151.25f, +572.746f, 151.25f, 572.746f, 10, 0, 0, 0, 0, 0, 0, +0, 5, 132.246f, 579.746f, 132.246f, 579.746f, 152.457f, 576.039f, 152.25f, 570.746f, +152.457f, 567.996f, 152.191f, 553.234f, 146.25f, 554.746f, 137.059f, 557.559f, 141.02f, 573.398f, +132.246f, 579.746f, 10, 0, 0.6f, 0.8f, 0.2f, 0.6f, 0.8f, 0.2f, +5, 141.25f, 575.75f, 141.25f, 575.75f, 151.332f, 574.195f, 152.25f, 570.746f, 153.117f, +569.438f, 153.848f, 560.301f, 148.246f, 558.75f, 142.832f, 558.098f, 140.379f, 569.34f, 141.25f, +575.75f, 10, 0, 0, 0, 0, 0, 0, 0, 44, +236.25f, 528.746f, 235.504f, 530.93f, 236.949f, 530.785f, 239.25f, 531.75f, 241.117f, 532.039f, +254.539f, 536.219f, 255.246f, 538.75f, 256.297f, 541.938f, 271.25f, 536.75f, 271.25f, 536.75f, +272.797f, 536.219f, 277.246f, 533.746f, 277.246f, 533.746f, 282.918f, 532.039f, 290.246f, 531.75f, +290.246f, 531.75f, 292.816f, 530.5f, 296.25f, 527.75f, 296.25f, 527.75f, 312.617f, 516.199f, +326.25f, 523.75f, 326.25f, 523.75f, 348.258f, 531.379f, 341.25f, 550.746f, 341.25f, 550.746f, +338.359f, 560.199f, 342.246f, 563.746f, 342.246f, 563.746f, 342.098f, 568.117f, 350.25f, 560.75f, +350.25f, 560.75f, 352.879f, 556.457f, 354.246f, 550.746f, 354.246f, 550.746f, 362.559f, 538.637f, +359.25f, 557.746f, 359.25f, 557.746f, 359.039f, 559.316f, 355.961f, 563.277f, 356.246f, 564.75f, +355.961f, 565.918f, 354.246f, 569.75f, 354.246f, 569.75f, 350.68f, 573.398f, 353.246f, 580.75f, +353.246f, 580.75f, 355.301f, 596.277f, 353.246f, 594.746f, 353.246f, 594.746f, 351.559f, 596.277f, +341.25f, 585.746f, 341.25f, 585.746f, 339.02f, 581.539f, 332.246f, 579.746f, 332.246f, 579.746f, +329.34f, 577.797f, 325.25f, 579.746f, 325.25f, 579.746f, 322.738f, 579.777f, 316.25f, 571.75f, +316.25f, 571.75f, 319.66f, 572.297f, 322.301f, 567.457f, 325.25f, 566.75f, 327.578f, 567.02f, +329.559f, 569.879f, 331.246f, 570.746f, 333.078f, 571.199f, 336.25f, 564.75f, 336.25f, 564.75f, +336.598f, 561.957f, 330.25f, 556.75f, 330.25f, 556.75f, 330, 551.617f, 328.25f, 553.75f, +328.25f, 553.75f, 324.938f, 554.039f, 323.621f, 549.859f, 322.246f, 544.746f, 321.418f, 539.738f, +317.25f, 539.746f, 317.25f, 539.746f, 315.039f, 531.156f, 313.246f, 534.75f, 313.246f, 534.75f, +313.5f, 540.617f, 307.25f, 533.746f, 307.25f, 533.746f, 305.578f, 532.039f, 300.246f, 534.75f, +300.246f, 534.75f, 293.039f, 536.656f, 295.25f, 538.75f, 295.25f, 538.75f, 297.656f, 541.277f, +310.246f, 538.75f, 310.246f, 538.75f, 312.398f, 540.617f, 303.25f, 544.746f, 303.25f, 544.746f, +302.937f, 547, 304.25f, 551.75f, 304.25f, 551.75f, 305.359f, 555.359f, 313.246f, 561.746f, +313.246f, 561.746f, 323.18f, 562.84f, 320.246f, 564.75f, 320.246f, 564.75f, 313.277f, 570.316f, +307.25f, 561.746f, 307.25f, 561.746f, 304.477f, 555.137f, 285.25f, 538.75f, 285.25f, 538.75f, +280.059f, 534.898f, 282.918f, 542.379f, 278.246f, 538.75f, 274.117f, 534.898f, 251.25f, 544.746f, +251.25f, 544.746f, 238.738f, 546.109f, 235.734f, 528.793f, 232.246f, 531.75f, 232.246f, 531.75f, +237.813f, 522.855f, 236.25f, 528.746f, 10, 0, 0, 0, 0, 0, +0, 0, 12, 450.25f, 711.746f, 450.25f, 711.746f, 422.18f, 703.199f, 419.246f, +682.746f, 419.246f, 682.742f, 416.461f, 657.438f, 439.25f, 637.75f, 439.25f, 637.75f, 439.34f, +631.039f, 441.246f, 627.746f, 441.246f, 627.742f, 439.777f, 622.238f, 460.246f, 630.75f, 490.25f, +639.75f, 490.25f, 639.75f, 497.418f, 642.477f, 503.25f, 651.746f, 508.859f, 661.84f, 525.578f, +682.52f, 521.25f, 709.75f, 521.25f, 709.75f, 522.938f, 722.559f, 516.25f, 722.746f, 516.25f, +722.746f, 507.098f, 724.758f, 499.25f, 716.75f, 499.25f, 716.75f, 491.699f, 712.879f, 489.246f, +713.746f, 450.25f, 711.746f, 10, 0, 0, 0, 0, 0, 0, +0, 8, 510.25f, 712.75f, 510.25f, 712.75f, 512.73f, 722.91f, 507.25f, 717.746f, +507.25f, 717.742f, 499.664f, 711.293f, 491.246f, 711.746f, 491.242f, 711.746f, 475.465f, 708.875f, +470.25f, 694.75f, 470.25f, 694.75f, 466.266f, 664.828f, 475.25f, 658.746f, 475.25f, 658.746f, +480.305f, 650.309f, 488.25f, 657.75f, 495.793f, 664.828f, 512.844f, 698.082f, 510.25f, 712.75f, +10, 0, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 8, 510.25f, +712.75f, 510.25f, 712.75f, 512.309f, 722.313f, 507.25f, 716.75f, 507.25f, 716.75f, 499.48f, +710.906f, 491.246f, 710.746f, 491.242f, 710.742f, 475.719f, 708.531f, 471.246f, 694.75f, 471.242f, +694.75f, 466.691f, 665.289f, 475.25f, 658.746f, 475.25f, 658.746f, 480.469f, 651.031f, 488.25f, +657.75f, 495.68f, 665.289f, 512.387f, 697.961f, 510.25f, 712.75f, 10, 0, 0.4f, +0.4f, 0.4f, 0.4f, 0.4f, 0.4f, 8, 509.246f, 712.75f, 509.246f, 712.75f, +511.887f, 721.715f, 507.25f, 716.75f, 507.25f, 716.75f, 499.293f, 710.52f, 491.246f, 710.746f, +491.242f, 710.742f, 475.973f, 708.188f, 471.246f, 693.746f, 471.242f, 693.742f, 467.113f, 665.746f, +475.25f, 659.75f, 475.25f, 659.75f, 480.637f, 651.754f, 488.25f, 658.746f, 495.563f, 665.746f, +511.93f, 697.84f, 509.246f, 712.75f, 10, 0, 0.6f, 0.6f, 0.6f, 0.6f, +0.6f, 0.6f, 8, 509.246f, 711.746f, 509.246f, 711.746f, 511.465f, 721.113f, 506.246f, +715.746f, 506.242f, 715.742f, 499.109f, 710.133f, 491.246f, 709.75f, 491.242f, 709.75f, 476.23f, +707.844f, 471.246f, 693.746f, 471.242f, 693.742f, 467.535f, 666.203f, 476.246f, 660.746f, 476.246f, +660.742f, 480.805f, 652.477f, 488.25f, 659.75f, 495.449f, 666.203f, 511.477f, 697.719f, 509.246f, +711.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 8, +509.246f, 711.746f, 509.246f, 711.746f, 511.043f, 720.516f, 506.246f, 715.746f, 506.242f, 715.742f, +498.926f, 709.746f, 491.246f, 709.75f, 491.242f, 709.75f, 476.484f, 707.5f, 472.25f, 693.746f, +472.25f, 693.742f, 467.957f, 666.66f, 476.246f, 660.746f, 476.246f, 660.742f, 480.973f, 653.195f, +488.25f, 659.75f, 495.332f, 666.66f, 511.02f, 697.598f, 509.246f, 711.746f, 10, 0, +1, 1, 1, 1, 1, 1, 8, 508.25f, 710.746f, 508.25f, +710.742f, 510.621f, 719.918f, 506.246f, 714.75f, 506.242f, 714.75f, 498.738f, 709.359f, 491.246f, +709.75f, 491.242f, 709.75f, 476.738f, 707.156f, 472.25f, 693.746f, 472.25f, 693.742f, 468.379f, +667.117f, 476.246f, 661.75f, 476.246f, 661.75f, 481.141f, 653.918f, 488.25f, 660.746f, 495.219f, +667.117f, 510.563f, 697.477f, 508.25f, 710.746f, 10, 0, 0.6f, 0.15f, 0, +0.6f, 0.15f, 0, 24, 275.246f, 487.75f, 275.246f, 487.75f, 253.219f, 508.719f, +244.246f, 509.75f, 244.246f, 509.75f, 206.578f, 514, 190.246f, 493.746f, 190.246f, 493.742f, +209.656f, 516.637f, 240.25f, 510.746f, 240.25f, 510.742f, 216.258f, 515.316f, 202.246f, 511.746f, +202.242f, 511.746f, 184.137f, 511.797f, 173.25f, 496.75f, 170.246f, 490.75f, 170.242f, 490.75f, +174.898f, 507.398f, 195.25f, 513.746f, 195.25f, 513.746f, 220.219f, 519.277f, 232.246f, 513.746f, +232.246f, 513.746f, 208.34f, 521.477f, 197.25f, 519.746f, 197.25f, 519.742f, 163.898f, 521.918f, +150.246f, 492.75f, 150.246f, 492.75f, 154.219f, 508.719f, 170.246f, 516.75f, 170.242f, 516.75f, +185.457f, 526.316f, 208.25f, 522.746f, 208.25f, 522.746f, 223.738f, 519.719f, 229.25f, 516.75f, +235.18f, 514.438f, 233.859f, 517.52f, 224.246f, 522.746f, 224.242f, 522.746f, 218.457f, 533.797f, +203.25f, 533.746f, 203.25f, 533.746f, 155.977f, 529.398f, 144.246f, 515.746f, 144.246f, 515.742f, +159.5f, 528.52f, 171.246f, 531.75f, 171.246f, 531.75f, 195.578f, 540.398f, 205.25f, 539.746f, +205.25f, 539.742f, 232.098f, 538.418f, 240.25f, 542.75f, 240.25f, 542.75f, 228.137f, 537.316f, +231.246f, 533.746f, 235.18f, 530.277f, 242.656f, 521.918f, 242.246f, 520.75f, 242.656f, 519.277f, +269.277f, 494.969f, 273.25f, 489.746f, 275.246f, 487.75f, 10, 0, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 5, 428.25f, 273.746f, 428.25f, 273.742f, 410.848f, +314.348f, 397.246f, 324.746f, 397.246f, 324.746f, 425.699f, 307.199f, 429.25f, 287.75f, 429.25f, +287.75f, 429.547f, 276.398f, 428.25f, 273.746f, 10, 0, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 7, 479.25f, 265.75f, 479.25f, 265.75f, 450.449f, 326.449f, +430.246f, 352.746f, 430.246f, 352.742f, 477.949f, 311.598f, 483.25f, 282.746f, 484.246f, 276.75f, +480.246f, 278.75f, 480.242f, 278.75f, 480.148f, 269.25f, 479.25f, 265.75f, 10, 0, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 542.246f, 319.746f, 542.246f, +319.742f, 473, 384.75f, 471.246f, 387.746f, 471.242f, 387.742f, 537.898f, 314.898f, 541.25f, +306.746f, 541.25f, 306.742f, 539, 316.547f, 542.246f, 319.746f, 10, 0, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 334.246f, 271.746f, 334.246f, 271.746f, +355.848f, 328.648f, 377.246f, 303.75f, 377.246f, 303.75f, 393.25f, 292.898f, 392.25f, 289.75f, +392.25f, 289.75f, 388.297f, 296.75f, 368.246f, 295.746f, 368.242f, 295.742f, 347.598f, 299.5f, +334.246f, 271.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +5, 544.246f, 411.746f, 544.246f, 411.742f, 494.449f, 443.047f, 486.25f, 444.746f, 473.211f, +447.297f, 540.648f, 412.797f, 546.246f, 400.75f, 546.242f, 400.75f, 548.348f, 404, 544.246f, +411.746f, 10, 0, 0, 0, 0, 0, 0, 0, 63, +378.246f, 294.75f, 378.246f, 294.75f, 400.621f, 296.637f, 408.246f, 303.75f, 413.246f, 299.746f, +432.246f, 342.75f, 436.25f, 336.75f, 436.25f, 336.75f, 452.098f, 352.957f, 451.25f, 361.746f, +450.34f, 370.559f, 465.246f, 354.746f, 465.246f, 354.746f, 465.246f, 354.742f, 464.418f, 367.918f, +472.25f, 360.75f, 472.25f, 360.75f, 469.699f, 377.598f, 479.25f, 368.746f, 479.25f, 368.746f, +467.348f, 401.969f, 492.25f, 373.75f, 498.301f, 366.598f, 493.246f, 373.75f, 493.246f, 373.75f, +493.242f, 373.75f, 464.859f, 426.879f, 488.25f, 410.75f, 488.25f, 410.75f, 490.82f, 436.117f, +489.246f, 440.746f, 488.18f, 445.797f, 485.98f, 470.438f, 480.246f, 475.746f, 475.418f, 481.879f, +481.141f, 483.637f, 487.246f, 477.746f, 487.246f, 477.742f, 474.98f, 504.316f, 489.246f, 490.75f, +489.246f, 490.75f, 485.539f, 507.84f, 480.246f, 510.746f, 480.242f, 510.742f, 474.539f, 529.84f, +491.246f, 517.746f, 491.242f, 517.742f, 486.418f, 531.598f, 483.25f, 534.75f, 483.25f, 534.75f, +470.141f, 565.477f, 478.246f, 559.746f, 483.25f, 555.75f, 483.25f, 555.75f, 475.418f, 571.637f, +482.246f, 566.75f, 489.5f, 561.957f, 489.246f, 562.75f, 489.246f, 562.75f, 489.246f, 562.75f, +466.18f, 598.918f, 488.25f, 579.746f, 488.25f, 579.746f, 479.645f, 594.867f, 476.246f, 602.75f, +476.246f, 602.75f, 455.18f, 624.879f, 471.246f, 617.75f, 476.246f, 615.75f, 476.246f, 615.75f, +466.621f, 627.078f, 458.246f, 628.75f, 449.02f, 630.598f, 460.461f, 637.637f, 467.246f, 635.75f, +474.539f, 633.238f, 491.246f, 624.75f, 491.246f, 624.75f, 491.242f, 624.75f, 505.777f, 604.199f, +510.25f, 603.746f, 510.25f, 603.746f, 488.18f, 612.117f, 495.246f, 603.746f, 495.242f, 603.746f, +510.621f, 587.918f, 502.246f, 588.75f, 502.242f, 588.75f, 496.098f, 580.438f, 501.25f, 570.746f, +501.25f, 570.746f, 481.074f, 590.988f, 497.25f, 562.75f, 505.25f, 544.746f, 505.25f, 544.746f, +478.059f, 572.078f, 490.25f, 547.75f, 490.25f, 547.75f, 509.301f, 521.918f, 511.246f, 521.746f, +513.699f, 521.039f, 518.25f, 511.746f, 518.25f, 511.746f, 513.246f, 513.746f, 519.25f, 503.75f, +519.25f, 503.75f, 507.098f, 517.078f, 513.246f, 502.746f, 520.246f, 486.746f, 520.246f, 486.742f, +497.418f, 510.918f, 512.25f, 478.746f, 512.25f, 478.746f, 494.34f, 484.078f, 504.246f, 464.746f, +504.242f, 464.742f, 502.258f, 447.559f, 502.246f, 441.75f, 503.141f, 436.117f, 504.461f, 404.879f, +499.25f, 395.75f, 494.777f, 387.277f, 506.219f, 366.156f, 508.25f, 361.746f, 510.621f, 357.355f, +514.578f, 345.477f, 505.25f, 355.75f, 495.219f, 365.719f, 500.059f, 359.559f, 502.246f, 349.75f, +504.461f, 340.195f, 511.059f, 323.035f, 510.25f, 316.75f, 510.25f, 316.75f, 508.859f, 315.559f, +505.25f, 319.746f, 505.25f, 319.742f, 489.059f, 344.598f, 491.246f, 328.746f, 491.242f, 328.742f, +489.5f, 319.957f, 486.25f, 310.746f, 486.25f, 310.742f, 482.461f, 298.398f, 482.246f, 307.75f, +482.242f, 307.75f, 478.938f, 326.559f, 476.246f, 317.746f, 472.777f, 309.836f, 468.82f, 303.238f, +465.246f, 300.75f, 462.66f, 297.957f, 456.938f, 323.035f, 455.25f, 311.75f, 455.25f, 311.75f, +442.418f, 325.238f, 437.25f, 306.746f, 424.246f, 288.746f, 424.242f, 288.742f, 423.938f, 302.797f, +422.246f, 295.746f, 422.246f, 295.742f, 389.621f, 289.598f, 378.246f, 294.75f, 10, 0, +0, 0, 0, 0, 0, 0, 34, 340.25f, 686.746f, 340.25f, +686.742f, 327.578f, 695.719f, 323.246f, 695.746f, 318.777f, 694.84f, 353.539f, 704.957f, 399.246f, +674.75f, 399.246f, 674.75f, 404.141f, 671.52f, 408.246f, 671.746f, 408.246f, 671.742f, 411.621f, +669.316f, 408.246f, 665.75f, 408.246f, 665.75f, 398.859f, 654.797f, 411.246f, 642.746f, 411.246f, +642.742f, 431.418f, 635, 425.25f, 644.75f, 425.25f, 644.75f, 437.141f, 640.277f, 440.25f, +635.75f, 442.418f, 631.477f, 441.246f, 635.75f, 441.246f, 635.75f, 441.246f, 635.75f, 434.059f, +643.797f, 427.25f, 649.746f, 427.25f, 649.742f, 421.738f, 651.719f, 418.25f, 660.746f, 415.578f, +670.199f, 412.938f, 681.199f, 418.25f, 684.746f, 418.25f, 684.742f, 413.379f, 679.879f, 414.25f, +684.746f, 415.141f, 688.68f, 419.098f, 692.637f, 421.246f, 692.75f, 422.621f, 693.52f, 440.66f, +710.898f, 448.25f, 711.746f, 448.25f, 711.746f, 438.02f, 709.797f, 434.246f, 710.746f, 431.418f, +712, 402.16f, 724.539f, 395.25f, 725.75f, 395.25f, 725.75f, 377.078f, 733.117f, 390.246f, +730.746f, 390.242f, 730.742f, 429.66f, 726.738f, 449.25f, 711.746f, 449.25f, 711.746f, 441.758f, +721.457f, 421.246f, 728.746f, 421.246f, 728.742f, 397.098f, 743.02f, 358.25f, 737.746f, 358.25f, +737.742f, 338.801f, 734, 330.25f, 731.75f, 330.25f, 731.75f, 327.359f, 732.68f, 326.25f, +732.746f, 326.039f, 733.559f, 313.059f, 743.457f, 282.25f, 735.746f, 282.25f, 735.746f, 264, +730.699f, 254.246f, 725.75f, 254.246f, 725.75f, 237.816f, 724.098f, 234.246f, 720.75f, 234.246f, +720.75f, 213.398f, 704.52f, 211.246f, 703.75f, 209, 702.758f, 196.457f, 694.398f, 195.25f, +693.746f, 195.25f, 693.742f, 222.637f, 701.219f, 225.25f, 703.75f, 227.918f, 706.5f, 247.059f, +709.359f, 249.25f, 707.75f, 252.34f, 706.277f, 261.578f, 706.938f, 251.25f, 706.746f, 251.25f, +706.742f, 334.18f, 690, 335.246f, 687.75f, 335.938f, 685.598f, 340.25f, 686.746f, 340.25f, +686.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 13, +419.246f, 696.75f, 419.246f, 696.75f, 407.66f, 705.18f, 405.25f, 704.746f, 403.258f, 705.18f, +389.621f, 716.398f, 385.25f, 715.746f, 380.379f, 715.52f, 366.961f, 726.52f, 337.25f, 717.746f, +337.25f, 717.742f, 336.16f, 719.699f, 340.25f, 720.75f, 340.25f, 720.75f, 347.16f, 723, +347.25f, 723.75f, 347.25f, 723.75f, 369.82f, 728.277f, 377.246f, 724.746f, 377.246f, 724.746f, +387.859f, 721.457f, 394.25f, 714.75f, 394.25f, 714.75f, 407, 711.117f, 410.246f, 711.746f, +410.246f, 711.746f, 420.199f, 709.797f, 420.246f, 707.75f, 420.246f, 707.75f, 427.02f, 704.52f, +425.25f, 701.75f, 425.25f, 701.75f, 425.48f, 699.898f, 419.246f, 696.75f, 10, 0, +0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 38, 405.25f, 699.746f, 406.047f, +698.664f, 407.168f, 698.555f, 408.246f, 697.746f, 408.094f, 697.32f, 407.773f, 696.961f, 407.25f, +696.75f, 406.281f, 696.504f, 405.121f, 697.133f, 404.25f, 696.75f, 403.422f, 696.258f, 402.715f, +696.457f, 402.246f, 696.75f, 400.313f, 697.105f, 398.301f, 697.137f, 396.25f, 696.75f, 394.254f, +697.621f, 391.66f, 696.977f, 389.246f, 697.746f, 389.309f, 698.109f, 389.063f, 697.727f, 389.246f, +697.746f, 385.629f, 699.016f, 381.512f, 698.707f, 379.246f, 700.746f, 376.168f, 701.672f, 373.574f, +702.18f, 371.25f, 702.746f, 368.906f, 703.488f, 367.355f, 704.574f, 365.246f, 705.75f, 364.059f, +706.27f, 362.457f, 706.844f, 361.25f, 707.75f, 358.719f, 707.75f, 356.703f, 707.625f, 354.246f, +707.75f, 354.52f, 708.227f, 354.309f, 707.848f, 354.246f, 707.75f, 353.863f, 707.996f, 353.543f, +708.637f, 353.246f, 708.746f, 351.508f, 708.004f, 349.871f, 709.074f, 348.25f, 708.746f, 346.742f, +710.043f, 344.844f, 709.773f, 343.246f, 710.746f, 339.883f, 711.195f, 336.414f, 709.797f, 333.246f, +710.746f, 337.602f, 712.926f, 342.758f, 711.57f, 347.25f, 713.746f, 349.789f, 715.148f, 352.715f, +713.938f, 355.246f, 714.75f, 356.078f, 714.934f, 356.84f, 715.152f, 357.246f, 714.75f, 357.426f, +714.566f, 357.625f, 714.828f, 357.246f, 714.75f, 360.387f, 713.527f, 362.934f, 712.125f, 365.246f, +710.746f, 366.039f, 710.793f, 366.621f, 711.047f, 367.246f, 710.746f, 368.57f, 709.488f, 370.711f, +709.602f, 372.25f, 708.746f, 374.105f, 708.809f, 376.078f, 708.391f, 378.246f, 708.746f, 378.066f, +709.109f, 378.324f, 708.734f, 378.246f, 708.746f, 379.602f, 709.582f, 380.875f, 709.281f, 382.25f, +708.746f, 382.227f, 708.82f, 382.957f, 708.551f, 383.25f, 708.746f, 384.531f, 708.164f, 385.473f, +707.637f, 387.246f, 707.75f, 386.895f, 707.414f, 387.098f, 707.789f, 387.246f, 707.75f, 388.41f, +707.277f, 389.559f, 707.34f, 390.246f, 705.75f, 390.426f, 706.207f, 390.609f, 706.469f, 390.246f, +706.746f, 391.828f, 706.066f, 392.543f, 705.238f, 394.25f, 704.746f, 394.289f, 704.855f, 394.961f, +704.168f, 395.25f, 703.75f, 398.227f, 703.168f, 400.254f, 701.488f, 402.246f, 700.746f, 403.5f, +700.16f, 404.465f, 699.902f, 405.25f, 699.746f, 10, 0, 0.8f, 0.45f, 0.15f, +0.8f, 0.45f, 0.15f, 23, 321.246f, 714.75f, 318.094f, 716.91f, 315.488f, 718.125f, +313.246f, 719.746f, 312.605f, 720.234f, 312.207f, 720.051f, 312.246f, 719.746f, 310.879f, 720.852f, +309.902f, 721.492f, 309.246f, 722.746f, 308.227f, 722.68f, 307.324f, 722.664f, 307.25f, 722.746f, +303.969f, 724.371f, 301.074f, 724.984f, 298.246f, 726.746f, 299.066f, 727, 300.301f, 726.73f, +301.246f, 727.75f, 301.172f, 727.309f, 301.434f, 726.996f, 302.246f, 726.746f, 303.668f, 728.203f, +305.703f, 728.371f, 307.25f, 728.746f, 309.422f, 728.172f, 311.313f, 727.836f, 313.246f, 727.75f, +313.605f, 727.484f, 313.824f, 726.91f, 314.25f, 726.746f, 316.629f, 726.074f, 319.258f, 726.648f, +321.246f, 725.75f, 323.336f, 725.035f, 325.066f, 724.133f, 326.25f, 722.746f, 326.703f, 722.441f, +326.348f, 722.113f, 326.25f, 721.746f, 326.465f, 722.02f, 326.766f, 721.793f, 327.25f, 721.746f, +326.98f, 721.184f, 326.98f, 720.852f, 327.25f, 720.75f, 326.766f, 720.246f, 326.457f, 720.133f, +326.25f, 719.746f, 324.5f, 719.871f, 326.449f, 721.387f, 325.25f, 720.75f, 324.277f, 720, +325.098f, 718.453f, 324.246f, 716.75f, 323.973f, 717.27f, 323.719f, 717.512f, 324.246f, 717.746f, +324.098f, 717.363f, 323.434f, 717.043f, 323.246f, 716.75f, 322.824f, 715.898f, 321.836f, 714.344f, +321.246f, 714.75f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, +25, 285.25f, 706.746f, 281.645f, 707.801f, 278.293f, 707.676f, 275.246f, 708.746f, 274.898f, +709.109f, 274.656f, 708.727f, 274.25f, 708.746f, 273.082f, 709.41f, 272.07f, 710.473f, 271.25f, +711.746f, 269.883f, 712.602f, 268.059f, 712.176f, 266.246f, 712.75f, 266.301f, 712.848f, 266.078f, +713.41f, 266.246f, 713.746f, 264.406f, 713.625f, 263.387f, 714.672f, 262.25f, 715.746f, 264.809f, +716.172f, 267.461f, 716.137f, 270.25f, 716.75f, 270.293f, 716.582f, 270.453f, 716.227f, 270.25f, +715.746f, 270.746f, 716.227f, 270.891f, 716.469f, 271.25f, 716.75f, 271.254f, 716.309f, 271.586f, +715.953f, 272.25f, 715.746f, 272.469f, 716.824f, 273.082f, 716.617f, 273.25f, 716.75f, 273.836f, +716.563f, 273.973f, 716.227f, 274.25f, 715.746f, 274.27f, 716.227f, 274.41f, 716.57f, 274.25f, +716.75f, 274.707f, 716.57f, 274.852f, 716.227f, 275.246f, 715.746f, 275.148f, 716.227f, 275.289f, +716.469f, 275.246f, 716.75f, 276.199f, 715.758f, 277.172f, 716.367f, 278.246f, 715.746f, 279.219f, +715.922f, 279.512f, 714.656f, 280.246f, 714.75f, 285.879f, 712.895f, 290.43f, 710.535f, 295.25f, +707.75f, 295.566f, 708.078f, 295.801f, 707.805f, 295.25f, 707.75f, 295.973f, 707.379f, 296.316f, +707.477f, 296.25f, 707.75f, 297.688f, 706.523f, 298.832f, 705.922f, 299.246f, 704.746f, 299.84f, +704.34f, 299.477f, 703.895f, 299.246f, 703.75f, 294.348f, 705.047f, 289.941f, 705.715f, 285.25f, +706.746f, 10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 10, +270.25f, 658.746f, 268.117f, 659.637f, 267.477f, 661.871f, 266.246f, 663.75f, 266, 664.215f, +266.301f, 664.563f, 266.246f, 664.746f, 267.266f, 664.832f, 267.863f, 664.309f, 268.246f, 663.75f, +270.234f, 663.137f, 271.922f, 661.77f, 274.25f, 661.75f, 276.309f, 659.16f, 280.992f, 658.738f, +281.25f, 654.75f, 281, 654.074f, 279.43f, 655.082f, 279.246f, 653.746f, 276.262f, 655.242f, +273.633f, 655.129f, 271.25f, 656.746f, 270.336f, 657.16f, 270.699f, 657.66f, 270.25f, 658.746f, +10, 0, 0.8f, 0.45f, 0.15f, 0.8f, 0.45f, 0.15f, 6, 239.25f, +715.746f, 239.727f, 716.129f, 247.461f, 715.871f, 247.246f, 715.746f, 247.391f, 715.406f, 238.891f, +714.254f, 238.25f, 714.75f, 238.309f, 714.523f, 230.047f, 711.852f, 230.25f, 711.746f, 230.191f, +712.148f, 239.285f, 716.129f, 239.25f, 715.746f, 10, 0, 0, 0, 0, +0, 0, 0, 80, 256.246f, 705.75f, 256.246f, 705.75f, 240.238f, 703.418f, +235.246f, 701.75f, 230.559f, 700.777f, 210.098f, 692.418f, 207.25f, 689.746f, 207.25f, 689.746f, +192.059f, 684.277f, 173.25f, 662.746f, 173.25f, 662.742f, 181.719f, 666.02f, 184.25f, 668.75f, +184.25f, 668.75f, 199.098f, 682.957f, 199.246f, 679.75f, 199.246f, 679.75f, 212.297f, 689.559f, +211.246f, 686.746f, 211.246f, 686.742f, 238.477f, 699.457f, 236.25f, 695.746f, 236.25f, 695.742f, +260.039f, 701, 259.25f, 698.75f, 259.25f, 698.75f, 279.617f, 693.957f, 276.246f, 693.746f, +276.246f, 693.742f, 270.156f, 692.418f, 277.246f, 688.746f, 277.246f, 688.742f, 273.457f, 683.617f, +267.246f, 687.75f, 261.578f, 692.418f, 264.879f, 690, 259.25f, 688.746f, 259.25f, 688.742f, +256.52f, 688.02f, 251.25f, 692.75f, 251.25f, 692.75f, 245.297f, 697.477f, 235.246f, 693.746f, +235.242f, 693.742f, 201.957f, 679.656f, 200.246f, 678.746f, 200.246f, 678.746f, 195.797f, 675.918f, +193.25f, 671.746f, 193.25f, 671.742f, 186.777f, 667.117f, 183.25f, 665.75f, 183.25f, 665.75f, +169.398f, 652.816f, 168.25f, 651.746f, 168.25f, 651.742f, 164.34f, 645.559f, 163.25f, 644.75f, +163.25f, 644.75f, 170.5f, 649.297f, 172.25f, 651.746f, 172.25f, 651.742f, 188.098f, 662.5f, +194.25f, 663.75f, 194.25f, 663.75f, 198.879f, 666.68f, 200.246f, 667.746f, 200.246f, 667.746f, +215.598f, 678.34f, 220.246f, 678.746f, 220.246f, 678.746f, 230.34f, 672.617f, 233.246f, 680.746f, +233.246f, 680.746f, 239.359f, 682.297f, 245.246f, 680.746f, 245.246f, 680.746f, 249.039f, 683.84f, +248.25f, 686.746f, 248.25f, 686.742f, 249.918f, 688.238f, 251.25f, 683.75f, 251.25f, 683.75f, +254.758f, 680.098f, 260.25f, 682.746f, 260.25f, 682.742f, 264.437f, 682.52f, 262.25f, 679.75f, +262.25f, 679.75f, 257.398f, 675.699f, 244.246f, 675.746f, 244.246f, 675.742f, 230.777f, 674.816f, +212.246f, 666.75f, 212.246f, 666.75f, 179.957f, 655.02f, 170.246f, 643.75f, 170.242f, 643.75f, +162.797f, 633.898f, 157.25f, 632.75f, 157.25f, 632.75f, 150.477f, 631.699f, 144.246f, 623.746f, +144.246f, 623.746f, 154.656f, 629.938f, 164.246f, 629.746f, 164.242f, 629.742f, 168.957f, 632.578f, +165.246f, 628.75f, 165.242f, 628.75f, 160.816f, 620.258f, 162.25f, 614.746f, 162.25f, 614.746f, +161.918f, 608.598f, 161.246f, 606.75f, 161.246f, 606.75f, 152.457f, 592.758f, 152.25f, 589.75f, +152.457f, 587.477f, 153.777f, 576.699f, 154.246f, 575.75f, 154.656f, 575.379f, 153.117f, 577.797f, +157.25f, 574.746f, 161.477f, 572.52f, 164.559f, 570.758f, 165.246f, 567.75f, 166.316f, 564.598f, +163.238f, 573.617f, 163.25f, 575.75f, 162.797f, 577.578f, 158.18f, 585.5f, 159.246f, 587.746f, +159.242f, 587.742f, 160.156f, 587.039f, 161.246f, 585.746f, 161.246f, 585.742f, 160.379f, 586.156f, +161.246f, 589.75f, 161.246f, 589.75f, 161.918f, 595.84f, 163.25f, 599.75f, 165, 602.879f, +167.199f, 607.059f, 167.25f, 607.746f, 168.078f, 608.816f, 168.078f, 615.199f, 169.25f, 612.746f, +173.25f, 609.746f, 173.25f, 609.742f, 170.277f, 612.34f, 172.25f, 614.746f, 172.25f, 614.746f, +171.598f, 620.918f, 173.25f, 623.746f, 173.25f, 623.746f, 181.277f, 633.02f, 183.25f, 633.75f, +184.797f, 635.219f, 183.25f, 634.746f, 183.25f, 634.746f, 183.25f, 634.746f, 189.859f, 639.398f, +183.25f, 637.75f, 183.25f, 637.75f, 179.078f, 635.879f, 176.246f, 635.75f, 176.246f, 635.75f, +167.418f, 633.68f, 172.25f, 638.746f, 176.219f, 642.918f, 187.219f, 648.859f, 191.246f, 648.75f, +192.25f, 646.75f, 204.25f, 649.746f, 203.25f, 648.75f, 203.25f, 648.75f, 203.059f, 648.859f, +207.25f, 649.746f, 212.297f, 649.738f, 218.68f, 648.199f, 220.246f, 649.746f, 221.758f, 652.156f, +225.5f, 653.258f, 225.25f, 651.746f, 224.617f, 650.18f, 224.246f, 647.746f, 224.246f, 647.746f, +224.242f, 647.746f, 229.898f, 654.359f, 229.25f, 651.746f, 228.578f, 649.52f, 219.559f, 643.797f, +218.25f, 636.746f, 229.25f, 645.746f, 233.246f, 649.746f, 233.246f, 649.742f, 237.379f, 646.879f, +237.25f, 648.75f, 237.816f, 650.398f, 242.879f, 656.777f, 244.246f, 656.746f, 245.52f, 656.34f, +247.719f, 659.418f, 247.246f, 656.746f, 247.277f, 653.699f, 255.246f, 647.746f, 255.246f, 647.746f, +255.246f, 647.746f, 259.156f, 649.738f, 260.25f, 647.746f, 262.238f, 646.656f, 267.246f, 669.746f, +267.246f, 669.746f, 294.25f, 681.75f, 342.246f, 685.75f, 323.246f, 692.75f, 256.246f, 705.75f, +10, 2.2f, 0.3f, 0, 0, 0.3f, 0, 0, 3, 276.246f, +486.746f, 276.246f, 486.742f, 260.039f, 504.977f, 251.25f, 507.75f, 251.25f, 507.75f, 236.059f, +515.316f, 209.246f, 506.746f, 10, 2.2f, 0.3f, 0, 0, 0.3f, 0, +0, 3, 247.246f, 509.75f, 247.242f, 509.75f, 219.559f, 518.18f, 202.246f, 513.746f, +202.242f, 513.746f, 182.379f, 511.359f, 173.25f, 495.746f, 10, 2.2f, 0.3f, 0, +0, 0.3f, 0, 0, 4, 243.246f, 510.746f, 243.246f, 510.742f, 224.617f, +518.617f, 208.25f, 520.75f, 208.25f, 520.75f, 190.078f, 523.898f, 172.25f, 515.746f, 172.25f, +515.742f, 158.398f, 509.379f, 152.25f, 497.746f, 10, 2.2f, 0.3f, 0, 0, +0.3f, 0, 0, 4, 244.246f, 510.746f, 244.246f, 510.742f, 227.477f, 522.359f, +226.25f, 523.75f, 226.25f, 523.75f, 218.68f, 536, 204.25f, 536.75f, 204.25f, 536.75f, +180.84f, 535.559f, 162.25f, 526.746f, 10, 0, 0, 0, 0, 0, +0, 0, 169, 243.246f, 519.746f, 244.68f, 518.543f, 274.25f, 486.746f, 274.25f, +486.746f, 313.059f, 446.457f, 282.25f, 483.75f, 282.25f, 483.75f, 273.898f, 489.359f, 264.246f, +509.75f, 264.246f, 509.75f, 262.457f, 513.117f, 279.246f, 501.75f, 279.246f, 501.75f, 283.578f, +501.238f, 298.246f, 479.75f, 298.246f, 479.75f, 291.059f, 482.758f, 296.25f, 474.75f, 296.25f, +474.75f, 299.418f, 472.637f, 322.246f, 455.746f, 322.246f, 455.746f, 325.82f, 451.078f, 330.25f, +449.746f, 330.25f, 449.746f, 345.621f, 455.035f, 338.25f, 440.746f, 338.25f, 440.746f, 341.219f, +433.035f, 347.25f, 445.746f, 347.25f, 445.746f, 359.699f, 464.277f, 341.25f, 461.75f, 341.25f, +461.75f, 308.656f, 458.559f, 301.246f, 475.746f, 301.246f, 475.746f, 298.539f, 478.797f, 308.246f, +475.746f, 308.246f, 475.746f, 317.461f, 473.957f, 300.246f, 489.746f, 300.246f, 489.746f, 302.937f, +489.797f, 313.246f, 482.746f, 313.246f, 482.746f, 324.5f, 472.199f, 326.25f, 474.75f, 326.25f, +474.75f, 346.5f, 484.078f, 358.25f, 475.746f, 358.25f, 475.746f, 360.141f, 473.957f, 353.98f, +466.477f, 355.246f, 460.746f, 357.5f, 455.035f, 363.25f, 441.75f, 363.25f, 441.75f, 360.141f, +439.637f, 360.25f, 427.746f, 360.25f, 427.746f, 379.059f, 402.238f, 368.246f, 404.75f, 368.246f, +404.75f, 351.34f, 404.879f, 367.246f, 396.746f, 367.246f, 396.746f, 371.141f, 394.316f, 381.25f, +386.75f, 381.25f, 386.75f, 377.738f, 387.719f, 376.246f, 381.746f, 376.246f, 381.746f, 381.258f, +377.598f, 378.246f, 372.746f, 378.246f, 372.746f, 371.578f, 370.996f, 370.25f, 366.75f, 370.25f, +366.75f, 377.738f, 357.797f, 366.246f, 357.746f, 366.246f, 357.746f, 370.699f, 352.516f, 365.246f, +339.746f, 365.246f, 339.746f, 360.141f, 339.316f, 353.246f, 332.746f, 353.246f, 332.746f, 355.738f, +327.879f, 344.246f, 321.746f, 344.246f, 321.746f, 335.059f, 319.957f, 338.25f, 312.75f, 338.25f, +312.75f, 329.34f, 305.879f, 326.25f, 288.746f, 326.25f, 288.746f, 325.82f, 276.836f, 323.18f, +273.316f, 329.25f, 275.746f, 334.621f, 277.719f, 333.246f, 291.746f, 333.246f, 291.746f, 328.461f, +308.516f, 375.246f, 325.75f, 375.246f, 325.75f, 379.938f, 327.879f, 381.25f, 333.75f, 381.25f, +333.75f, 383.02f, 333.156f, 392.25f, 324.746f, 392.25f, 324.746f, 401.059f, 312.477f, 401.246f, +322.75f, 401.246f, 322.75f, 402.82f, 326.559f, 401.246f, 332.746f, 401.246f, 332.746f, 407.66f, +356.918f, 392.25f, 363.746f, 392.25f, 363.746f, 381.258f, 400.918f, 396.25f, 391.75f, 396.25f, +391.75f, 399.738f, 385.516f, 411.246f, 379.746f, 411.246f, 379.746f, 415.25f, 382.75f, 413.82f, +387.719f, 423.246f, 394.746f, 423.246f, 394.746f, 426.141f, 387.277f, 432.246f, 395.75f, 432.246f, +395.75f, 436.699f, 422.918f, 450.25f, 406.75f, 450.25f, 406.75f, 454.738f, 405.758f, 456.246f, +412.746f, 456.246f, 412.746f, 460.02f, 424.676f, 456.246f, 439.75f, 456.246f, 439.75f, 460.02f, +440.078f, 470.25f, 433.746f, 470.25f, 433.746f, 473.66f, 437.438f, 463.539f, 455.918f, 468.25f, +453.746f, 472.34f, 450.637f, 477.25f, 448.75f, 477.25f, 448.75f, 478.059f, 451.078f, 467.246f, +464.746f, 467.246f, 464.746f, 462.219f, 467.797f, 456.246f, 489.746f, 456.246f, 489.746f, 464.418f, +486.277f, 453.25f, 502.746f, 453.25f, 502.746f, 453.418f, 506.52f, 460.246f, 518.75f, 460.246f, +518.75f, 459.141f, 526.316f, 460.246f, 525.75f, 460.246f, 525.75f, 463.098f, 524.559f, 471.898f, +522.797f, 464.25f, 529.75f, 456.938f, 536.879f, 465.246f, 541.746f, 465.246f, 541.746f, 470.141f, +545.238f, 455.25f, 544.746f, 455.25f, 544.746f, 449.461f, 549.637f, 450.25f, 553.75f, 450.25f, +553.75f, 458.699f, 551.84f, 442.859f, 567.68f, 440.25f, 571.75f, 437.578f, 575.598f, 448.25f, +581.746f, 448.25f, 581.746f, 462.66f, 585.277f, 450.25f, 588.75f, 450.25f, 588.75f, 428.34f, +588.359f, 440.25f, 599.75f, 440.25f, 599.75f, 446.82f, 599.797f, 445.246f, 602.75f, 445.246f, +602.75f, 439.34f, 603.758f, 429.25f, 610.75f, 429.25f, 610.75f, 424.379f, 614.758f, 428.25f, +613.75f, 428.25f, 613.75f, 446.82f, 612.559f, 415.25f, 624.75f, 415.25f, 624.75f, 423.938f, +624.879f, 404.25f, 636.746f, 404.25f, 636.746f, 401.938f, 638.078f, 398.246f, 646.75f, 398.246f, +646.75f, 391.82f, 652.156f, 386.246f, 659.75f, 386.246f, 659.75f, 386.098f, 664.477f, 381.25f, +669.746f, 381.25f, 669.746f, 368.059f, 684.719f, 362.25f, 684.746f, 362.25f, 684.746f, 345.621f, +688.238f, 340.25f, 687.75f, 340.25f, 687.75f, 282.25f, 682.746f, 252.777f, 668.438f, 261.25f, +645.746f, 261.25f, 645.746f, 268.398f, 636.098f, 278.246f, 640.746f, 278.246f, 640.746f, 283.578f, +647.098f, 296.25f, 644.75f, 296.25f, 644.75f, 318.777f, 641.156f, 316.25f, 644.75f, 316.25f, +644.75f, 313.277f, 650.18f, 295.457f, 657, 295.25f, 657.75f, 295.02f, 658.316f, 285.25f, +661.75f, 285.25f, 661.75f, 282.039f, 663.379f, 277.246f, 673.746f, 277.246f, 673.746f, 273.68f, +677.238f, 291.246f, 670.75f, 291.246f, 670.75f, 289.738f, 669.758f, 298.246f, 665.75f, 298.246f, +665.75f, 317.02f, 666.457f, 328.25f, 654.75f, 328.25f, 654.75f, 340.559f, 636.316f, 341.25f, +645.746f, 341.25f, 645.746f, 343.859f, 655.68f, 331.246f, 678.746f, 331.246f, 678.746f, 331.32f, +681.199f, 340.25f, 673.746f, 340.25f, 673.746f, 341.879f, 676.137f, 343.246f, 669.746f, 343.246f, +669.746f, 342.98f, 667.117f, 347.25f, 658.746f, 347.25f, 658.746f, 350.238f, 644.02f, 354.246f, +651.746f, 354.246f, 651.746f, 359.25f, 641.75f, 360.801f, 638.957f, 354.246f, 630.75f, 354.246f, +630.75f, 353.98f, 627.52f, 354.859f, 627.738f, 348.25f, 619.75f, 342.539f, 611.02f, 346.246f, +605.746f, 346.246f, 605.746f, 344.738f, 598.918f, 354.246f, 599.75f, 354.246f, 599.75f, 357.277f, +596.938f, 361.25f, 596.746f, 361.25f, 596.746f, 363, 594.738f, 365.246f, 595.75f, 365.246f, +595.75f, 367.398f, 599.578f, 374.25f, 597.75f, 374.25f, 597.75f, 375.758f, 600.02f, 385.25f, +600.75f, 385.25f, 600.75f, 385.879f, 603.316f, 386.32f, 605.078f, 390.246f, 605.746f, 393.801f, +606.398f, 366.246f, 653.746f, 366.246f, 653.746f, 373.777f, 654.578f, 365.246f, 667.746f, 365.246f, +667.746f, 362.34f, 675.477f, 374.879f, 659.418f, 377.246f, 657.75f, 379.719f, 656.34f, 380.82f, +653.918f, 379.246f, 653.746f, 377.301f, 654.359f, 375.32f, 651.938f, 377.246f, 651.746f, 378.398f, +651.5f, 392.699f, 635, 396.25f, 623.746f, 400.18f, 612.559f, 406.777f, 608.156f, 413.246f, +601.746f, 420.418f, 594.957f, 419.246f, 568.746f, 419.246f, 568.746f, 419.098f, 558.656f, 425.25f, +546.746f, 425.25f, 546.746f, 427.898f, 542.816f, 423.246f, 522.746f, 423.246f, 522.746f, 421.078f, +520.379f, 422.246f, 519.746f, 422.246f, 519.746f, 423.719f, 518.18f, 431.246f, 503.75f, 431.246f, +503.75f, 429.219f, 503.879f, 433.246f, 499.746f, 433.246f, 499.746f, 438.898f, 493.316f, 432.246f, +496.75f, 432.246f, 496.75f, 425.258f, 498.379f, 433.246f, 487.75f, 433.246f, 487.75f, 434.277f, +485.617f, 424.246f, 490.75f, 424.246f, 490.75f, 414.258f, 491.34f, 427.25f, 483.75f, 427.25f, +483.75f, 436.48f, 475.5f, 424.246f, 480.746f, 424.246f, 480.746f, 418.879f, 482.316f, 422.246f, +474.75f, 422.246f, 474.75f, 425.918f, 473.078f, 445.246f, 465.75f, 445.246f, 465.75f, 445.277f, +461.195f, 442.246f, 455.746f, 442.246f, 455.746f, 442.418f, 451.297f, 440.25f, 447.746f, 440.25f, +447.746f, 438.68f, 438.535f, 438.25f, 437.75f, 438.25f, 437.75f, 433.398f, 437.438f, 425.25f, +422.746f, 425.25f, 422.746f, 423.277f, 419.398f, 412.246f, 405.746f, 412.246f, 405.746f, 409.859f, +398.496f, 390.246f, 406.75f, 390.246f, 406.75f, 382.801f, 402.676f, 385.25f, 406.75f, 385.25f, +406.75f, 384.559f, 408.836f, 390.246f, 415.75f, 390.246f, 415.75f, 397.539f, 418.297f, 394.25f, +429.746f, 394.25f, 429.746f, 399.078f, 431.719f, 386.758f, 434.797f, 387.246f, 435.75f, 387.199f, +437.438f, 393.25f, 438.746f, 393.25f, 438.746f, 402.379f, 441.176f, 397.246f, 443.75f, 397.246f, +443.75f, 396.879f, 448.219f, 400.246f, 454.75f, 400.246f, 454.75f, 412.938f, 455.258f, 400.246f, +472.75f, 400.246f, 472.75f, 388.301f, 481.438f, 387.246f, 487.75f, 387.246f, 487.75f, 401.059f, +496.84f, 392.039f, 510.477f, 392.25f, 514.75f, 392.48f, 518.398f, 394.25f, 541.746f, 394.25f, +541.746f, 391.598f, 548.977f, 388.246f, 563.746f, 388.246f, 563.746f, 390.719f, 569.656f, 399.246f, +583.746f, 399.246f, 583.746f, 401.938f, 588.137f, 411.621f, 593.418f, 409.246f, 596.746f, 406.777f, +600.02f, 398.246f, 597.75f, 398.246f, 597.75f, 389.621f, 599.578f, 390.246f, 593.75f, 390.246f, +593.75f, 388.52f, 592.758f, 387.246f, 587.746f, 387.246f, 587.746f, 386.848f, 578.531f, 377.246f, +571.75f, 377.246f, 571.75f, 364.758f, 564.816f, 375.246f, 560.75f, 375.246f, 560.75f, 381.48f, +553.156f, 370.25f, 552.746f, 370.25f, 552.746f, 358.598f, 554.918f, 367.246f, 543.746f, 367.246f, +543.746f, 379.5f, 529.617f, 376.246f, 526.746f, 376.246f, 526.746f, 364.98f, 525.438f, 379.246f, +515.746f, 379.246f, 515.746f, 379.242f, 515.742f, 377.961f, 517.52f, 378.246f, 515.746f, 378.398f, +513.559f, 381.699f, 508.938f, 382.25f, 506.746f, 383.461f, 504.539f, 379.246f, 504.746f, 379.246f, +504.746f, 379.719f, 493.758f, 363.25f, 498.75f, 363.25f, 498.75f, 363.25f, 498.75f, 362.777f, +498.379f, 361.25f, 497.746f, 359.258f, 497.938f, 346.938f, 498.816f, 340.25f, 500.746f, 334.18f, +503.656f, 326.25f, 503.75f, 326.25f, 503.75f, 326.25f, 503.75f, 322.301f, 501.68f, 314.25f, +501.75f, 305.578f, 502.117f, 297.246f, 498.75f, 297.246f, 498.75f, 291.937f, 499.477f, 301.398f, +504.316f, 301.246f, 503.75f, 301.84f, 503.879f, 308, 510.039f, 299.246f, 509.75f, 275.223f, +507.578f, 263.25f, 518.75f, 263.25f, 518.75f, 261.137f, 520.379f, 258.246f, 523.75f, 258.246f, +523.75f, 247.277f, 525.656f, 260.25f, 509.75f, 260.25f, 509.75f, 261.137f, 508.277f, 259.25f, +506.746f, 259.25f, 506.746f, 258.719f, 508.938f, 250.25f, 514.75f, 250.25f, 514.75f, 247.047f, +515.949f, 245.547f, 517.414f, 243.246f, 519.746f, 10, 0, 0.3f, 0, 0, +0.3f, 0, 0, 7, 216.25f, 532.746f, 216.25f, 532.742f, 229.457f, 526.758f, +232.246f, 523.75f, 235.18f, 520.598f, 250.25f, 507.75f, 250.25f, 507.75f, 250.25f, 507.75f, +244.637f, 510.258f, 242.246f, 511.746f, 238.918f, 514.219f, 227.25f, 522.746f, 227.25f, 522.746f, +227.25f, 522.746f, 222.859f, 529.84f, 216.25f, 532.746f, 10, 0, 0.6f, 0.8f, +0.2f, 0.6f, 0.8f, 0.2f, 6, 153.246f, 566.75f, 153.258f, 567.398f, 152.684f, +570.379f, 152.25f, 570.746f, 151.332f, 573.977f, 141.25f, 575.75f, 141.25f, 575.75f, 141.207f, +574.098f, 141.148f, 572.34f, 141.25f, 570.746f, 141.25f, 570.746f, 146.621f, 564.469f, 153.246f, +566.75f, 10, 0, 0.4f, 0.6f, 0, 0.4f, 0.6f, 0, 6, +153.246f, 567.75f, 152.395f, 567.281f, 152.871f, 570.461f, 152.25f, 570.746f, 151.555f, 573.977f, +141.25f, 575.75f, 141.25f, 575.75f, 141.207f, 574.207f, 141.148f, 572.449f, 141.25f, 570.746f, +141.25f, 570.746f, 145.961f, 565.02f, 153.246f, 567.75f, 10, 0, 0, 0, +0, 0, 0, 0, 6, 148.246f, 567.75f, 147.371f, 567.297f, 146.812f, +568.551f, 147.25f, 569.75f, 146.812f, 571.645f, 147.371f, 572.898f, 148.246f, 572.746f, 148.746f, +572.898f, 149.305f, 571.645f, 149.246f, 569.75f, 149.305f, 568.551f, 148.746f, 567.297f, 148.246f, +567.75f, 10, 0, 0, 0, 0, 0, 0, 0, 17, +98.2461f, 459.75f, 98.2422f, 459.75f, 91.7383f, 448.438f, 119.25f, 454.75f, 119.25f, 454.75f, +134.418f, 456.355f, 137.246f, 458.746f, 138.379f, 458.117f, 147.582f, 454.891f, 150.246f, 453.746f, +158.18f, 452.398f, 167.25f, 463.75f, 167.25f, 463.75f, 167.25f, 463.75f, 172.477f, 474.949f, +175.25f, 474.75f, 178.637f, 474.949f, 175.25f, 472.75f, 175.25f, 472.75f, 175.25f, 472.75f, +167.859f, 462.078f, 168.25f, 460.746f, 168.25f, 460.742f, 162.578f, 438.316f, 145.25f, 437.75f, +145.25f, 437.75f, 127.215f, 436.391f, 128.25f, 429.746f, 128.25f, 429.742f, 138.379f, 432.598f, +141.25f, 429.746f, 141.25f, 429.742f, 152.898f, 430.398f, 144.246f, 423.746f, 136.25f, 410.75f, +136.25f, 410.75f, 136.773f, 406.289f, 125.25f, 409.746f, 114.84f, 413.898f, 103.25f, 427.746f, +103.25f, 427.746f, 103.25f, 427.742f, 85.9648f, 444.094f, 98.2461f, 459.75f, 10, 0, +0.9f, 0.6f, 0.6f, 0.9f, 0.6f, 0.6f, 14, 96.25f, 454.75f, 96.25f, +454.75f, 94.3789f, 444.477f, 135.25f, 455.746f, 135.25f, 455.742f, 139.699f, 455.918f, 142.246f, +454.75f, 144.977f, 454.156f, 158.18f, 451.078f, 160.246f, 452.75f, 160.246f, 452.75f, 152.457f, +437.438f, 139.246f, 438.746f, 139.246f, 438.742f, 125.18f, 437.438f, 125.25f, 431.746f, 125.25f, +431.742f, 130.02f, 424.238f, 135.25f, 421.75f, 135.25f, 421.75f, 138.379f, 418.957f, 138.246f, +415.75f, 137.5f, 411.918f, 134.418f, 410.156f, 132.246f, 409.746f, 130.02f, 408.398f, 126.5f, +411.918f, 124.25f, 411.746f, 122.977f, 411.918f, 113.738f, 418.957f, 109.246f, 423.746f, 104.059f, +429.516f, 94.8164f, 442.719f, 95.25f, 445.746f, 95.6992f, 448.879f, 96.25f, 454.75f, 96.25f, +454.75f, 10, 0, 0.7f, 0.4f, 0.4f, 0.7f, 0.4f, 0.4f, 13, +100.246f, 435.75f, 102.957f, 431.496f, 106.477f, 426.879f, 109.246f, 423.746f, 113.738f, 418.957f, +122.977f, 411.918f, 124.25f, 411.746f, 126.5f, 411.918f, 130.02f, 408.398f, 132.246f, 409.746f, +134.418f, 410.156f, 137.5f, 411.918f, 138.246f, 415.75f, 138.379f, 418.957f, 135.25f, 421.75f, +135.25f, 421.75f, 131.926f, 423.285f, 128.91f, 427.125f, 127.246f, 429.746f, 127.246f, 429.742f, +127.379f, 426.879f, 121.246f, 427.746f, 115.938f, 428.637f, 110.219f, 431.719f, 108.25f, 434.746f, +106.699f, 438.758f, 104.059f, 441.398f, 106.25f, 437.75f, 107.578f, 433.477f, 110.219f, 429.516f, +112.25f, 428.75f, 113.738f, 428.637f, 113.297f, 427.316f, 110.246f, 427.746f, 108.02f, 428.195f, +104.938f, 428.637f, 100.246f, 434.746f, 10, 0, 0.6f, 0.15f, 0, 0.6f, +0.15f, 0, 20, 97.25f, 458.746f, 97.25f, 458.746f, 99.2187f, 473.957f, 100.246f, +478.746f, 100.246f, 478.746f, 99.6563f, 485.84f, 102.25f, 490.75f, 104.938f, 495.078f, 107.137f, +501.898f, 110.246f, 507.75f, 113.738f, 513.777f, 113.957f, 518.18f, 118.25f, 519.746f, 122.758f, +521.699f, 129.359f, 531.156f, 132.246f, 532.746f, 135.52f, 533.359f, 135.25f, 532.746f, 135.25f, +532.746f, 135.25f, 532.742f, 142.777f, 548.758f, 157.25f, 544.746f, 157.25f, 544.746f, 139.918f, +547.438f, 157.25f, 557.746f, 157.25f, 557.746f, 152.02f, 556.566f, 155.246f, 564.75f, 158.07f, +569.402f, 157.52f, 561.957f, 145.25f, 548.746f, 145.25f, 548.746f, 139.918f, 539.52f, 134.25f, +535.746f, 128.477f, 532.918f, 115.277f, 525.219f, 114.25f, 520.75f, 112.637f, 516.859f, 109.117f, +510.477f, 107.25f, 508.746f, 104.719f, 506.957f, 101.637f, 502.34f, 101.25f, 498.75f, 101.25f, +498.75f, 99.8789f, 494.199f, 98.2461f, 492.75f, 96.7969f, 491.559f, 96.5781f, 488.039f, 96.25f, +485.75f, 96.5781f, 483.637f, 94.3789f, 480.559f, 94.2461f, 477.746f, 94.2461f, 477.742f, 95.4766f, +457.016f, 95.25f, 454.75f, 97.25f, 458.746f, 10, 0, 1, 1, 1, +1, 1, 1, 6, 88.2461f, 453.746f, 88.2461f, 453.742f, 85.5781f, 455.477f, +80.25f, 448.75f, 80.25f, 448.75f, 88.7695f, 412.578f, 89.2461f, 410.75f, 89.2461f, 410.75f, +89.9766f, 413.348f, 88.2461f, 421.75f, 87.1172f, 429.188f, 86.25f, 442.746f, 86.25f, 442.746f, +88.2461f, 453.746f, 10, 0, 0.6f, 0.15f, 0, 0.6f, 0.15f, 0, +13, 111.246f, 520.75f, 111.246f, 520.75f, 92.1797f, 517.078f, 92.2461f, 484.746f, 91.25f, +457.75f, 91.25f, 457.75f, 90.418f, 485.84f, 89.2461f, 487.75f, 87.7773f, 489.359f, 92.1797f, +501.68f, 88.2461f, 494.75f, 88.2461f, 494.75f, 73.2578f, 479.68f, 82.2461f, 456.746f, 82.2422f, +456.746f, 83.707f, 452.727f, 80.25f, 457.75f, 80.25f, 457.75f, 75.3477f, 471.648f, 76.2461f, +478.746f, 76.2422f, 478.746f, 76.7773f, 481.109f, 79.25f, 483.75f, 79.25f, 483.75f, 88.3281f, +497.059f, 91.25f, 499.746f, 91.25f, 499.742f, 93.2773f, 515.43f, 110.246f, 520.75f, 110.246f, +520.75f, 116.488f, 523.68f, 111.246f, 520.75f, 10, 0, 0, 0, 0, +0, 0, 0, 28, 265.246f, 593.75f, 265.605f, 593.809f, 265.594f, 594.875f, +266.246f, 594.746f, 267.496f, 595.441f, 267.676f, 596.617f, 268.246f, 597.75f, 269.207f, 598.93f, +269.418f, 600.617f, 270.25f, 602.75f, 270.359f, 603.027f, 270.387f, 604.078f, 270.25f, 604.75f, +268.754f, 607.531f, 267.98f, 610.227f, 266.246f, 612.746f, 266.098f, 613.391f, 265.812f, 614.262f, +265.246f, 614.746f, 265.082f, 616.441f, 263.699f, 617.535f, 263.25f, 618.746f, 262.434f, 619.469f, +263.012f, 620.488f, 262.25f, 620.746f, 261.238f, 620.695f, 259.645f, 621.332f, 259.25f, 619.75f, +258.742f, 617.359f, 259.852f, 614.586f, 261.25f, 611.75f, 260.059f, 611.137f, 260.426f, 610.125f, +260.25f, 609.746f, 261.375f, 605.313f, 260.055f, 601.625f, 259.25f, 597.75f, 259.191f, 597.691f, +259.57f, 597.473f, 259.25f, 597.75f, 258.195f, 594.449f, 256.598f, 591.762f, 254.246f, 588.75f, +253.762f, 588.051f, 252.805f, 587.043f, 252.25f, 585.746f, 251.852f, 585.008f, 251.402f, 583.945f, +251.25f, 582.75f, 247.898f, 579.801f, 245.426f, 575.57f, 242.246f, 571.75f, 242.043f, 570.594f, +242.363f, 569.262f, 243.246f, 568.746f, 243.863f, 568.527f, 244.918f, 569.656f, 245.246f, 570.746f, +245.859f, 571.352f, 246.25f, 572.066f, 247.246f, 572.746f, 246.937f, 572.969f, 246.738f, 573.43f, +247.246f, 573.75f, 249.785f, 576.145f, 251.621f, 579.375f, 254.246f, 581.746f, 256.465f, 582.34f, +258.152f, 583.438f, 260.25f, 584.75f, 260.414f, 584.75f, 260.992f, 584.477f, 261.25f, 584.75f, +263.238f, 585.984f, 263.238f, 588.223f, 263.25f, 590.746f, 263.41f, 591.297f, 263.625f, 592.746f, +265.246f, 593.75f, 10, 0, 0, 0, 0, 0, 0, 0, +19, 255.246f, 598.746f, 255.289f, 598.414f, 255.117f, 598.879f, 255.246f, 598.746f, 255.418f, +599.477f, 255.859f, 599.68f, 256.246f, 599.75f, 256.16f, 600.277f, 255.98f, 600.695f, 256.246f, +600.75f, 258.695f, 603.543f, 258.977f, 606.867f, 258.246f, 609.746f, 258.965f, 610.82f, 259.031f, +612.207f, 258.246f, 612.746f, 257.625f, 615.012f, 257.414f, 617.129f, 256.246f, 618.746f, 255.457f, +620.223f, 253.723f, 621.59f, 252.25f, 619.75f, 251.75f, 619.719f, 251.398f, 618.852f, 251.25f, +617.75f, 251.773f, 617.887f, 252.09f, 617.727f, 252.25f, 617.75f, 251.941f, 617.281f, 251.34f, +617.035f, 251.25f, 616.746f, 251.301f, 615.09f, 250.25f, 613.43f, 251.25f, 611.75f, 251.793f, +610.176f, 252.695f, 608.133f, 253.246f, 605.746f, 252.082f, 603.852f, 253.219f, 601.156f, 251.25f, +598.746f, 251.141f, 598.93f, 251.148f, 598.508f, 251.25f, 598.746f, 251.605f, 597.75f, 252.051f, +597.305f, 252.25f, 596.746f, 252.809f, 596.848f, 253.191f, 596.848f, 253.246f, 596.746f, 254.047f, +597.383f, 254.484f, 597.918f, 255.246f, 598.746f, 10, 0, 0, 0, 0, +0, 0, 0, 21, 324.246f, 609.746f, 325.773f, 607.703f, 326.094f, 604.629f, +324.246f, 602.75f, 324.445f, 599.457f, 328.129f, 601.637f, 330.25f, 601.746f, 330.32f, 602.645f, +330.57f, 603.023f, 331.246f, 602.75f, 332.043f, 603.047f, 332.789f, 604.18f, 334.246f, 603.746f, +334.438f, 605.691f, 336.238f, 606.465f, 337.25f, 607.746f, 338.844f, 612.047f, 338.195f, 616.746f, +335.246f, 620.746f, 335.129f, 620.598f, 335.371f, 621.164f, 335.246f, 621.75f, 334.402f, 623.996f, +332.125f, 624.34f, 330.25f, 624.75f, 328.703f, 629.359f, 327.977f, 633.793f, 326.25f, 637.75f, +324.266f, 638.133f, 323.496f, 640.047f, 322.246f, 640.746f, 320.559f, 641.629f, 319.934f, 639.891f, +320.246f, 638.746f, 319.988f, 638.516f, 320.484f, 638.27f, 320.246f, 637.75f, 320.215f, 637.688f, +319.926f, 637.566f, 320.246f, 637.75f, 319.93f, 637.27f, 320.172f, 637.125f, 320.246f, 636.746f, +319.309f, 636.074f, 317.742f, 635.551f, 317.25f, 634.746f, 316.371f, 630.211f, 319.199f, 626.773f, +321.246f, 623.746f, 321.684f, 622.004f, 320.875f, 620.605f, 320.246f, 619.75f, 319.559f, 618.512f, +319.676f, 617.273f, 320.246f, 616.746f, 320.809f, 613.766f, 322.559f, 611.855f, 324.246f, 609.746f, +10, 0, 0, 0, 0, 0, 0, 0, 73, 283.25f, +589.75f, 281.734f, 587.41f, 277.98f, 584.586f, 281.25f, 582.75f, 281.402f, 582.324f, 281.809f, +582.32f, 282.25f, 582.75f, 284.223f, 584.188f, 286.426f, 585.18f, 289.246f, 585.746f, 289.242f, +585.852f, 289.543f, 585.34f, 290.246f, 585.746f, 291.727f, 586.289f, 293.938f, 586.227f, 295.25f, +587.746f, 299.383f, 587.453f, 303.305f, 588.68f, 307.25f, 589.75f, 308.309f, 590.609f, 309.707f, +591.227f, 311.246f, 591.75f, 312.543f, 592.41f, 313.867f, 593.434f, 315.25f, 594.746f, 315.234f, +594.836f, 315.625f, 594.738f, 316.25f, 594.746f, 315.875f, 595.688f, 316.934f, 595.828f, 317.25f, +596.746f, 317.305f, 596.766f, 317.141f, 597.203f, 317.25f, 597.75f, 319.641f, 599.105f, 320.652f, +601.328f, 319.25f, 603.746f, 319.051f, 604.578f, 318.777f, 605.258f, 318.25f, 605.746f, 316.961f, +606.781f, 315.75f, 605.844f, 314.25f, 605.746f, 314.422f, 605.488f, 313.621f, 605.676f, 313.246f, +605.746f, 312.25f, 604.977f, 310.785f, 605.621f, 310.246f, 604.75f, 308.344f, 604.371f, 306.977f, +604.188f, 305.25f, 603.746f, 305.07f, 603.684f, 304.215f, 603.789f, 304.25f, 602.75f, 303.891f, +603.246f, 303.727f, 603.504f, 303.25f, 603.746f, 301.512f, 603.043f, 300.125f, 602.809f, 298.246f, +600.75f, 298.582f, 600.801f, 298.098f, 600.996f, 298.246f, 600.75f, 296.867f, 599.961f, 296.422f, +598.602f, 295.25f, 597.75f, 294.992f, 597.727f, 294.602f, 597.914f, 294.25f, 597.75f, 293.68f, +597.297f, 293.277f, 596.59f, 292.25f, 595.75f, 292.207f, 595.848f, 291.766f, 596.207f, 292.25f, +596.746f, 292.07f, 598.629f, 292.789f, 600.594f, 292.25f, 602.75f, 294.441f, 605.43f, 297.211f, +607.574f, 299.246f, 610.75f, 299.215f, 612.961f, 299.977f, 615.32f, 300.246f, 617.75f, 299.84f, +617.816f, 299.523f, 618.625f, 299.246f, 618.746f, 299.043f, 619.945f, 300.039f, 621.117f, 299.246f, +621.75f, 297.566f, 623.238f, 296.145f, 622.273f, 295.25f, 620.746f, 293.215f, 620.27f, 290.945f, +619.508f, 289.246f, 620.746f, 288.102f, 621.73f, 287.465f, 622.727f, 286.246f, 623.746f, 285.504f, +625.32f, 285.871f, 626.898f, 286.246f, 628.75f, 285.953f, 628.762f, 285.609f, 628.91f, 285.25f, +628.75f, 285.609f, 629.207f, 285.852f, 629.352f, 286.246f, 629.746f, 285.223f, 630.188f, 284.918f, +631.352f, 284.25f, 631.746f, 284.133f, 632.898f, 283.391f, 633.871f, 282.25f, 633.75f, 280.238f, +634.965f, 278.395f, 632.859f, 276.246f, 632.75f, 275.746f, 632.758f, 275.23f, 633.902f, 274.25f, +634.746f, 274.043f, 634.496f, 273.27f, 634.531f, 273.25f, 633.75f, 272.113f, 633.688f, 271.465f, +633.559f, 270.25f, 633.75f, 268.852f, 632.855f, 267.449f, 631.652f, 266.246f, 630.75f, 264.188f, +629.77f, 263.137f, 628.188f, 262.25f, 626.75f, 260.914f, 625.469f, 260.762f, 622.813f, 262.25f, +622.75f, 264.352f, 621.547f, 265.785f, 624.523f, 268.246f, 623.746f, 268.293f, 624.105f, 268.52f, +623.766f, 268.246f, 623.746f, 268.824f, 623.219f, 269.066f, 623.469f, 269.246f, 623.746f, 270.223f, +622.656f, 271.504f, 622.285f, 272.25f, 621.75f, 273.602f, 620.332f, 275.523f, 620.793f, 276.246f, +619.75f, 278.32f, 618.043f, 277.707f, 615.094f, 280.246f, 613.75f, 279.195f, 612.215f, 278.527f, +610.809f, 278.246f, 608.75f, 277.848f, 607.914f, 278.941f, 606.598f, 280.246f, 606.75f, 281.656f, +606.801f, 281.945f, 607.633f, 282.25f, 608.75f, 282.773f, 608.523f, 283.289f, 608.199f, 283.25f, +607.746f, 282.738f, 605.336f, 281.609f, 603.141f, 281.25f, 600.75f, 281.043f, 600.121f, 280.707f, +599.898f, 280.246f, 599.75f, 279.762f, 595.453f, 275.305f, 592.82f, 272.25f, 589.75f, 272.063f, +588.785f, 272.059f, 587.414f, 272.25f, 586.75f, 274.051f, 585.445f, 276.207f, 587.145f, 278.246f, +587.746f, 278.313f, 589.023f, 279.258f, 590.063f, 280.246f, 589.75f, 281.004f, 589.988f, 281.262f, +590.586f, 281.25f, 590.746f, 282, 590.879f, 282.555f, 590.633f, 283.25f, 590.746f, 284.77f, +592.164f, 286.32f, 593.383f, 288.246f, 594.746f, 288.441f, 594.832f, 288.82f, 594.66f, 289.246f, +594.746f, 289.414f, 594.957f, 289.621f, 595.383f, 290.246f, 595.75f, 290.359f, 595.805f, 290.625f, +595.484f, 291.246f, 594.746f, 290.129f, 594.793f, 290.125f, 593.742f, 289.246f, 593.75f, 288.629f, +593.223f, 288.012f, 592.66f, 287.246f, 591.75f, 286.949f, 591.957f, 286.227f, 592.23f, 286.246f, +591.75f, 285.453f, 590.902f, 284.152f, 590.418f, 283.25f, 589.75f, 10, 0, 0, +0, 0, 0, 0, 0, 30, 222.246f, 643.75f, 222.246f, 643.75f, +212.258f, 646.957f, 200.246f, 618.746f, 200.246f, 618.742f, 197.34f, 613, 194.25f, 610.75f, +192.059f, 608.598f, 179.738f, 604.637f, 177.246f, 599.75f, 166.246f, 582.75f, 166.246f, 582.75f, +182.379f, 600.238f, 186.25f, 602.75f, 186.25f, 602.75f, 194.699f, 612.117f, 191.246f, 604.75f, +191.242f, 604.75f, 175.777f, 592.758f, 177.246f, 582.75f, 177.246f, 582.75f, 170.938f, 566.797f, +170.246f, 564.75f, 170.242f, 564.75f, 187.656f, 599.797f, 190.246f, 600.75f, 192.938f, 602.438f, +194.258f, 602.438f, 193.25f, 598.746f, 191.617f, 594.52f, 191.18f, 576.477f, 188.246f, 574.746f, +188.246f, 574.742f, 196.898f, 596.719f, 196.25f, 599.75f, 196.25f, 599.75f, 199.539f, 604.199f, +202.246f, 598.746f, 201.246f, 580.75f, 205.25f, 567.75f, 205.25f, 567.75f, 203.059f, 580, +205.25f, 596.746f, 205.25f, 596.742f, 202.617f, 608.598f, 207.25f, 602.75f, 211.418f, 596.277f, +221.977f, 589.68f, 222.246f, 584.75f, 222.246f, 584.75f, 216.258f, 603.758f, 206.25f, 608.75f, +201.246f, 602.75f, 200.246f, 604.75f, 200.246f, 604.75f, 196.457f, 605.52f, 201.246f, 612.746f, +206.137f, 620.477f, 205.25f, 621.75f, 205.25f, 621.75f, 205.25f, 621.75f, 212.738f, 613.438f, +214.25f, 613.75f, 214.25f, 613.75f, 229.02f, 621.797f, 230.25f, 594.746f, 230.25f, 594.742f, +237.816f, 610.797f, 227.25f, 618.746f, 227.25f, 618.742f, 211.418f, 620.477f, 212.246f, 625.746f, +220.246f, 639.75f, 224.617f, 645.559f, 223.246f, 642.746f, 223.246f, 642.746f, 10, 0, +0, 0, 0, 0, 0, 0, 6, 200.246f, 625.746f, 200.246f, +625.746f, 186.34f, 625.758f, 183.25f, 619.75f, 175.25f, 609.746f, 175.25f, 609.742f, 193.816f, +620.477f, 198.246f, 621.75f, 202.617f, 623.117f, 200.246f, 625.746f, 200.246f, 625.746f, 10, +0, 0, 0, 0, 0, 0, 0, 7, 156.25f, 618.746f, +156.25f, 618.742f, 154.219f, 617.398f, 154.246f, 614.746f, 153.34f, 611.238f, 150.699f, 610.797f, +151.25f, 607.746f, 152.457f, 604.637f, 154.656f, 602, 154.246f, 606.75f, 154.656f, 610.797f, +156.418f, 613, 157.25f, 614.746f, 158.18f, 615.637f, 159.938f, 620.477f, 156.25f, 618.746f, +10, 0, 0, 0, 0, 0, 0, 0, 10, 146.25f, +551.75f, 146.25f, 551.75f, 137.5f, 555.797f, 134.25f, 559.746f, 130.457f, 563.719f, 130.957f, +558.035f, 125.25f, 558.75f, 119.187f, 558.922f, 120.246f, 576.746f, 120.246f, 576.746f, 116.246f, +567.75f, 116.246f, 567.75f, 114.617f, 552.277f, 123.25f, 554.746f, 127.715f, 556.207f, 129.137f, +554.477f, 127.246f, 553.75f, 125.617f, 552.719f, 133.539f, 552.277f, 130.25f, 550.746f, 127.379f, +548.758f, 143.219f, 554.477f, 140.25f, 542.75f, 146.25f, 551.75f, 10, 0, 0, +0, 0, 0, 0, 0, 8, 133.246f, 535.746f, 133.246f, 535.746f, +115.938f, 530.719f, 112.25f, 541.746f, 112.25f, 541.742f, 106.699f, 538.637f, 109.246f, 535.746f, +111.539f, 532.039f, 113.25f, 531.75f, 113.25f, 531.75f, 113.25f, 531.75f, 118.797f, 530.277f, +118.25f, 529.75f, 117.477f, 528.52f, 115.246f, 524.746f, 115.246f, 524.746f, 115.242f, 524.746f, +126.059f, 531.379f, 133.246f, 535.746f, 10, 0, 1, 1, 1, 1, +1, 1, 24, 384.25f, 449.746f, 383.648f, 447.191f, 381.813f, 446.309f, 379.246f, +445.746f, 377.609f, 446.629f, 374.754f, 450.047f, 372.25f, 447.746f, 372.156f, 448.305f, 371.301f, +448.371f, 371.25f, 448.75f, 370.41f, 450.086f, 370.711f, 451.238f, 370.25f, 451.746f, 369.734f, +453.516f, 368.953f, 455.016f, 369.25f, 456.746f, 371.145f, 457.359f, 371.801f, 459.457f, 371.25f, +461.75f, 371.203f, 461.68f, 370.73f, 461.895f, 371.25f, 462.746f, 371.156f, 462.633f, 371.504f, +462.883f, 372.25f, 462.746f, 371.652f, 463.031f, 371.492f, 462.773f, 371.25f, 462.746f, 370.699f, +462.91f, 370.836f, 463.613f, 371.25f, 463.75f, 371.621f, 465.957f, 373.836f, 466.25f, 375.246f, +464.746f, 375.602f, 465.559f, 376.16f, 465.348f, 376.246f, 465.75f, 376.586f, 466.016f, 377.031f, +466.594f, 377.246f, 466.746f, 377.82f, 468.266f, 379.613f, 467.047f, 380.25f, 467.746f, 381.672f, +468.629f, 382.844f, 469.398f, 384.25f, 468.75f, 386.02f, 467.621f, 387.898f, 466.285f, 389.246f, +464.746f, 389.848f, 463.453f, 390.113f, 462.047f, 390.246f, 460.746f, 390.008f, 460.281f, 388.488f, +460.668f, 388.246f, 459.75f, 387.402f, 457.723f, 389.414f, 457.152f, 390.246f, 455.746f, 390.465f, +455.297f, 390.176f, 454.961f, 390.246f, 454.75f, 389.375f, 454.711f, 388.516f, 454.922f, 388.246f, +454.75f, 389.734f, 450.91f, 386.703f, 450.164f, 384.25f, 449.746f, 10, 0, 1, +1, 1, 1, 1, 1, 11, 373.25f, 427.746f, 373.551f, 429.891f, +371.789f, 431.82f, 373.25f, 433.746f, 373.27f, 433.551f, 373.41f, 433.305f, 373.25f, 433.746f, +373.707f, 433.305f, 373.852f, 433.551f, 374.25f, 433.746f, 375.645f, 431.258f, 379.66f, 430.238f, +379.246f, 426.75f, 379.48f, 426.617f, 378.285f, 425.605f, 379.246f, 424.75f, 377.285f, 423.414f, +377.223f, 420.809f, 376.246f, 418.746f, 374.836f, 419.051f, 373.504f, 419.449f, 372.25f, 419.75f, +372.625f, 421.691f, 372.496f, 423.543f, 373.25f, 424.75f, 373.879f, 425.766f, 373.563f, 426.949f, +373.25f, 427.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +15, 190.246f, 437.75f, 190.246f, 437.75f, 172.195f, 426.727f, 187.246f, 443.75f, 197.34f, +454.156f, 208.25f, 460.746f, 208.25f, 460.746f, 208.25f, 460.742f, 219.777f, 465.156f, 223.246f, +466.746f, 227.699f, 467.797f, 244.418f, 473.52f, 248.25f, 473.746f, 251.457f, 474.398f, 262.02f, +478.797f, 269.246f, 474.75f, 276.977f, 470, 286.246f, 464.746f, 286.246f, 464.746f, 286.246f, +464.742f, 267.738f, 474.398f, 264.246f, 471.746f, 259.816f, 469.117f, 251.898f, 469.559f, 245.246f, +465.75f, 245.246f, 465.75f, 229.02f, 461.195f, 225.25f, 458.746f, 221.977f, 456.797f, 210.539f, +444.035f, 209.246f, 444.746f, 207.02f, 445.797f, 209.219f, 446.238f, 210.246f, 449.746f, 211.859f, +452.398f, 209.656f, 454.156f, 201.246f, 446.75f, 192.059f, 440.078f, 190.246f, 437.75f, 190.246f, +437.75f, 10, 0, 0, 0, 0, 0, 0, 0, 11, +199.246f, 444.746f, 199.246f, 444.742f, 200.434f, 458.785f, 210.246f, 456.746f, 210.246f, 456.746f, +218.809f, 461.539f, 222.246f, 463.75f, 222.246f, 463.75f, 230.758f, 465.578f, 232.246f, 466.746f, +252.523f, 475.824f, 268.715f, 470.855f, 269.246f, 471.746f, 269.918f, 473.316f, 291.504f, 465.488f, +295.25f, 460.746f, 295.906f, 460.508f, 284.219f, 467.152f, 273.25f, 468.75f, 264.453f, 471.008f, +240.691f, 468.961f, 228.25f, 462.746f, 225.422f, 461.211f, 215.582f, 454.848f, 213.246f, 454.75f, +210.016f, 455.094f, 199.246f, 444.746f, 199.246f, 444.746f, 10, 0, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 11, 194.25f, 416.746f, 194.25f, 416.742f, 177.977f, +418.957f, 196.25f, 420.746f, 196.25f, 420.742f, 216.258f, 422.918f, 220.246f, 428.75f, 220.246f, +428.75f, 235.617f, 438.758f, 238.25f, 438.746f, 241.777f, 439.637f, 274.777f, 447.559f, 275.246f, +449.746f, 275.656f, 452.836f, 281.816f, 452.836f, 283.25f, 451.746f, 285.34f, 451.078f, 284.457f, +449.758f, 281.25f, 448.75f, 278.297f, 447.996f, 243.977f, 429.957f, 237.25f, 428.75f, 229.898f, +427.316f, 217.137f, 418.957f, 212.246f, 417.75f, 206.578f, 416.316f, 194.25f, 416.746f, 194.25f, +416.746f, 10, 0, 0, 0, 0, 0, 0, 0, 11, +216.25f, 424.75f, 216.25f, 424.75f, 206.73f, 425.367f, 216.25f, 426.75f, 216.25f, 426.75f, +225.887f, 430.035f, 228.25f, 432.75f, 228.25f, 432.75f, 235.801f, 438.145f, 237.25f, 438.746f, +238.957f, 438.594f, 254.313f, 442.652f, 254.246f, 443.75f, 254.762f, 445.355f, 292.234f, 459.191f, +297.246f, 455.746f, 300.301f, 453.371f, 289.406f, 455.219f, 279.246f, 450.75f, 277.32f, 449.684f, +240.082f, 433.637f, 236.25f, 432.75f, 232.871f, 432.285f, 226.34f, 428.008f, 223.246f, 427.746f, +220.934f, 426.656f, 216.25f, 424.75f, 216.25f, 424.75f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 207.25f, 421.75f, 207.25f, 421.75f, 213.18f, +422.477f, 212.246f, 420.746f, 210.539f, 418.957f, 208.25f, 419.75f, 208.25f, 419.75f, 207.25f, +421.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +199.246f, 420.746f, 199.246f, 420.742f, 205.258f, 420.719f, 204.25f, 418.746f, 202.617f, 417.195f, +200.246f, 417.75f, 200.246f, 417.75f, 199.246f, 420.746f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 188.246f, 418.746f, 188.246f, 418.742f, 193.816f, +418.957f, 192.25f, 416.746f, 191.18f, 415.438f, 188.246f, 416.746f, 188.246f, 416.746f, 188.246f, +418.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +179.246f, 417.75f, 179.246f, 417.75f, 185.457f, 418.078f, 184.25f, 416.746f, 182.816f, 414.559f, +180.246f, 415.75f, 180.246f, 415.75f, 179.246f, 417.75f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 222.246f, 460.746f, 222.246f, 460.742f, 226.816f, +461.195f, 225.25f, 459.75f, 224.18f, 457.676f, 220.246f, 457.75f, 220.246f, 457.75f, 222.246f, +460.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +211.246f, 454.75f, 211.246f, 454.75f, 218.133f, 457.391f, 215.25f, 453.746f, 214.059f, 451.957f, +211.246f, 452.75f, 211.246f, 452.75f, 211.246f, 454.75f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 198.246f, 449.746f, 198.246f, 449.742f, 204.379f, +450.195f, 203.25f, 448.75f, 201.738f, 446.676f, 199.246f, 447.746f, 199.246f, 447.746f, 198.246f, +449.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +190.246f, 442.746f, 190.246f, 442.742f, 196.02f, 443.598f, 194.25f, 441.75f, 193.379f, 440.078f, +190.246f, 440.746f, 190.246f, 440.746f, 190.246f, 442.746f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 183.25f, 437.75f, 183.25f, 437.75f, 188.539f, +438.316f, 187.246f, 436.746f, 185.898f, 434.797f, 183.25f, 435.75f, 183.25f, 435.75f, 183.25f, +437.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +226.25f, 430.75f, 226.25f, 430.75f, 233.422f, 431.426f, 231.246f, 428.75f, 229.906f, 426.742f, +226.25f, 427.746f, 226.25f, 427.746f, 226.25f, 430.75f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 237.25f, 435.75f, 237.25f, 435.75f, 244.863f, +436.707f, 243.246f, 434.746f, 241.348f, 432.02f, 238.25f, 432.75f, 238.25f, 432.75f, 237.25f, +435.75f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +249.25f, 440.746f, 249.25f, 440.742f, 256.742f, 441.547f, 255.246f, 438.746f, 253.227f, 436.859f, +249.25f, 437.75f, 249.25f, 437.75f, 249.25f, 440.746f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 260.25f, 445.746f, 260.25f, 445.746f, 268.18f, +446.824f, 266.246f, 444.746f, 264.668f, 442.141f, 261.25f, 443.75f, 261.25f, 443.75f, 260.25f, +445.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +230.25f, 465.75f, 230.25f, 465.75f, 237.82f, 466.625f, 236.25f, 464.746f, 234.309f, 461.941f, +230.25f, 461.75f, 230.25f, 461.75f, 230.25f, 465.75f, 10, 0, 0, 0, +0, 0, 0, 0, 4, 241.25f, 469.746f, 241.25f, 469.746f, 248.82f, +470.145f, 247.246f, 467.746f, 245.309f, 465.461f, 240.25f, 465.75f, 240.25f, 465.75f, 241.25f, +469.746f, 10, 0, 0, 0, 0, 0, 0, 0, 4, +216.25f, 425.746f, 216.25f, 425.746f, 221.977f, 425.996f, 220.246f, 423.746f, 219.34f, 422.477f, +216.25f, 423.746f, 216.25f, 423.746f, 216.25f, 425.746f, 10, 0, 0.6f, 0.15f, +0, 0.6f, 0.15f, 0, 5, 135.25f, 534.75f, 135.25f, 534.75f, 130.898f, +525, 130.25f, 521.746f, 130.25f, 521.742f, 131.34f, 531.156f, 132.246f, 533.746f, 133.977f, +535.559f, 135.25f, 534.75f, 135.25f, 534.75f, 10, 0, 0.6f, 0.15f, 0, +0.6f, 0.15f, 0, 5, 115.246f, 519.746f, 115.242f, 519.742f, 111.977f, 503.438f, +112.25f, 500.746f, 112.25f, 500.746f, 111.098f, 513.117f, 111.246f, 514.75f, 111.977f, 515.758f, +115.246f, 519.746f, 115.246f, 519.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 6, 138.246f, 601.746f, 138.246f, 597.75f, 135.25f, 597.75f, 135.25f, +597.75f, 151.359f, 583.738f, 152.25f, 575.75f, 152.25f, 575.75f, 152.898f, 584.398f, 138.246f, +601.746f, 10, 0, 0, 0, 0, 0, 0, 0, 28, +143.246f, 599.75f, 142.285f, 600.402f, 142.527f, 601.223f, 142.246f, 601.746f, 141.188f, 602.078f, +143.508f, 602.141f, 143.246f, 602.75f, 142.836f, 604.254f, 143.039f, 604.277f, 143.246f, 605.746f, +142.844f, 606.34f, 143.488f, 608.031f, 144.246f, 608.75f, 145.504f, 610.332f, 144.047f, 613.559f, +146.25f, 615.75f, 146.188f, 615.582f, 146.598f, 616.191f, 147.25f, 616.746f, 147.637f, 617.711f, +148.941f, 618.246f, 150.246f, 618.746f, 150.336f, 619.461f, 150.113f, 620.371f, 150.246f, 620.746f, +151.523f, 620.141f, 152.891f, 620.285f, 153.246f, 619.75f, 152.715f, 617.027f, 151.254f, 615.137f, +150.246f, 613.75f, 150.344f, 612.527f, 149.84f, 611.828f, 149.246f, 610.75f, 148.059f, 608.336f, +148.266f, 605.207f, 148.246f, 601.746f, 148.07f, 601.992f, 147.73f, 601.906f, 147.25f, 601.746f, +148.129f, 599.277f, 148.77f, 596.859f, 149.246f, 594.746f, 150.141f, 593.387f, 150.66f, 592.402f, +151.25f, 591.75f, 150.945f, 590.625f, 151.059f, 589.711f, 150.246f, 588.75f, 152.848f, 585.754f, +151.41f, 582.84f, 152.25f, 578.75f, 152.922f, 578.27f, 154.785f, 576.164f, 154.246f, 576.746f, +151.512f, 577.297f, 151.387f, 577.734f, 151.25f, 578.75f, 151.031f, 579.25f, 150.668f, 580.762f, +150.246f, 581.746f, 150.336f, 581.605f, 150.148f, 583.68f, 150.246f, 583.746f, 148.398f, 586.434f, +149.895f, 586.238f, 148.246f, 588.75f, 146.816f, 589.582f, 145.754f, 590.797f, 144.246f, 591.75f, +144.301f, 592.297f, 145.559f, 593.094f, 145.25f, 593.75f, 144.156f, 594.746f, 142.887f, 595.59f, +143.246f, 596.746f, 143.43f, 597.992f, 143.578f, 599.156f, 143.246f, 599.75f, 10, 0, +0, 0, 0, 0, 0, 0, 11, 139.246f, 597.75f, 139.246f, +597.75f, 139.258f, 590.559f, 142.246f, 588.75f, 144.539f, 587.039f, 143.219f, 587.918f, 139.246f, +588.75f, 136.18f, 590.559f, 137.246f, 591.75f, 137.246f, 591.75f, 137.242f, 591.75f, 134.418f, +591, 137.246f, 588.75f, 139.699f, 586.598f, 143.656f, 583.957f, 142.246f, 583.746f, 140.137f, +583.957f, 131.777f, 588.359f, 132.246f, 591.75f, 131.777f, 594.52f, 130.25f, 598.746f, 130.25f, +598.746f, 130.25f, 598.742f, 131.887f, 599.906f, 137.246f, 599.75f, 137.242f, 599.75f, 138.707f, +599.027f, 139.246f, 597.75f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 138.246f, 577.75f, 138.246f, 577.75f, 128.566f, 580.648f, 108.25f, 576.746f, +108.25f, 576.742f, 118.172f, 579.203f, 139.246f, 576.746f, 150.148f, 575.324f, 138.246f, 577.75f, +138.246f, 577.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 138.246f, 577.75f, 138.246f, 577.75f, 128.566f, 580.648f, 108.25f, 576.746f, 108.25f, +576.742f, 118.172f, 579.203f, 139.246f, 576.746f, 150.148f, 575.324f, 138.246f, 577.75f, 138.246f, +577.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +140.25f, 577.75f, 140.25f, 577.75f, 131.176f, 581.527f, 110.246f, 579.746f, 110.246f, 579.746f, +120.695f, 580.984f, 141.25f, 576.746f, 152.215f, 574.355f, 140.25f, 577.75f, 140.25f, 577.75f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 140.25f, +577.75f, 140.25f, 577.75f, 131.176f, 581.527f, 110.246f, 579.746f, 110.246f, 579.746f, 120.695f, +580.984f, 141.25f, 576.746f, 152.215f, 574.355f, 140.25f, 577.75f, 140.25f, 577.75f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 142.246f, 577.75f, +142.242f, 577.75f, 133.453f, 582.086f, 113.25f, 581.746f, 113.25f, 581.746f, 122.965f, 582.328f, +143.246f, 576.746f, 153.902f, 573.371f, 142.246f, 577.75f, 142.246f, 577.75f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 142.246f, 577.75f, 142.242f, +577.75f, 133.453f, 582.086f, 113.25f, 581.746f, 113.25f, 581.746f, 122.965f, 582.328f, 143.246f, +576.746f, 153.902f, 573.371f, 142.246f, 577.75f, 142.246f, 577.75f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 143.246f, 577.75f, 143.242f, 577.75f, +136.102f, 582.047f, 117.25f, 583.746f, 117.25f, 583.742f, 126.715f, 583.066f, 144.246f, 576.746f, +153.77f, 572.66f, 143.246f, 577.75f, 143.246f, 577.75f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 143.246f, 577.75f, 143.242f, 577.75f, 136.102f, +582.047f, 117.25f, 583.746f, 117.25f, 583.742f, 126.715f, 583.066f, 144.246f, 576.746f, 153.77f, +572.66f, 143.246f, 577.75f, 143.246f, 577.75f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 270.25f, 565.746f, 270.25f, 565.742f, 269.398f, 565.031f, +269.246f, 566.75f, 269.871f, 567.629f, 300.898f, 582.117f, 305.25f, 581.746f, 305.25f, 581.746f, +271.602f, 567.316f, 270.25f, 565.746f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 270.25f, 565.746f, 270.25f, 565.742f, 269.398f, 565.031f, 269.246f, +566.75f, 269.871f, 567.629f, 300.898f, 582.117f, 305.25f, 581.746f, 305.25f, 581.746f, 271.602f, +567.316f, 270.25f, 565.746f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 266.246f, 564.75f, 266.246f, 564.75f, 265.727f, 564.25f, 266.246f, 565.746f, +265.992f, 566.879f, 295.785f, 583.758f, 300.246f, 583.746f, 300.246f, 583.742f, 267.742f, 566.699f, +266.246f, 564.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 266.246f, 564.75f, 266.246f, 564.75f, 265.727f, 564.25f, 266.246f, 565.746f, 265.992f, +566.879f, 295.785f, 583.758f, 300.246f, 583.746f, 300.246f, 583.742f, 267.742f, 566.699f, 266.246f, +564.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +263.25f, 563.746f, 263.25f, 563.742f, 262.164f, 562.676f, 262.25f, 563.746f, 262.254f, 565.316f, +284.055f, 582.363f, 295.25f, 584.75f, 295.25f, 584.75f, 275.016f, 575.484f, 263.25f, 563.746f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 263.25f, +563.746f, 263.25f, 563.742f, 262.164f, 562.676f, 262.25f, 563.746f, 262.254f, 565.316f, 284.055f, +582.363f, 295.25f, 584.75f, 295.25f, 584.75f, 275.016f, 575.484f, 263.25f, 563.746f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 260.25f, 561.746f, +260.25f, 561.742f, 259.09f, 560.711f, 259.25f, 561.746f, 259.176f, 563.086f, 278.793f, 578.43f, +288.246f, 580.75f, 288.246f, 580.75f, 270.656f, 572.238f, 260.25f, 561.746f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 260.25f, 561.746f, 260.25f, +561.742f, 259.09f, 560.711f, 259.25f, 561.746f, 259.176f, 563.086f, 278.793f, 578.43f, 288.246f, +580.75f, 288.246f, 580.75f, 270.656f, 572.238f, 260.25f, 561.746f, 10, 0, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 11, 225.25f, 398.746f, 225.25f, 398.742f, +208.34f, 401.355f, 227.25f, 402.75f, 227.25f, 402.75f, 246.617f, 405.316f, 251.25f, 410.75f, +251.25f, 410.75f, 265.977f, 421.156f, 269.246f, 421.75f, 272.137f, 422.035f, 290.18f, 425.996f, +290.246f, 428.75f, 291.059f, 431.277f, 297.656f, 433.918f, 299.246f, 432.75f, 301.18f, 432.156f, +301.18f, 422.035f, 298.246f, 420.746f, 295.02f, 420.277f, 274.34f, 412.355f, 267.246f, 410.75f, +260.258f, 409.719f, 247.5f, 401.355f, 242.246f, 399.75f, 236.938f, 398.719f, 225.25f, 398.746f, +225.25f, 398.746f, 10, 0, 0, 0, 0, 0, 0, 0, +11, 305.25f, 439.75f, 305.25f, 439.75f, 302.059f, 438.098f, 300.246f, 434.746f, 300.246f, +434.746f, 293.699f, 423.578f, 278.246f, 419.75f, 278.246f, 419.75f, 252.777f, 410.156f, 244.246f, +407.746f, 244.246f, 407.742f, 229.457f, 402.457f, 221.246f, 403.746f, 221.246f, 403.746f, 213.617f, +403.117f, 220.246f, 401.746f, 220.246f, 401.746f, 242.656f, 403.559f, 246.246f, 405.746f, 246.242f, +405.742f, 263.559f, 411.258f, 267.246f, 413.75f, 270.156f, 416.977f, 290.18f, 422.477f, 292.25f, +424.75f, 295.02f, 426.879f, 305.797f, 436.117f, 305.25f, 439.75f, 10, 0, 0, +0, 0, 0, 0, 0, 4, 241.25f, 404.75f, 241.25f, 404.75f, +246.52f, 405.445f, 245.246f, 403.746f, 243.984f, 402.035f, 241.25f, 402.75f, 241.25f, 402.75f, +241.25f, 404.75f, 10, 0, 0, 0, 0, 0, 0, 0, +4, 233.246f, 403.746f, 233.246f, 403.746f, 238.598f, 403.957f, 237.25f, 402.75f, 236.063f, +400.547f, 233.246f, 401.746f, 233.246f, 401.746f, 233.246f, 403.746f, 10, 0, 0, +0, 0, 0, 0, 0, 4, 221.246f, 402.75f, 221.246f, 402.75f, +227.125f, 402.586f, 226.25f, 400.75f, 224.59f, 399.176f, 222.246f, 399.75f, 222.246f, 399.75f, +221.246f, 402.75f, 10, 0, 0, 0, 0, 0, 0, 0, +4, 213.246f, 401.746f, 213.242f, 401.746f, 218.73f, 401.984f, 217.25f, 400.75f, 216.191f, +398.578f, 213.246f, 399.75f, 213.246f, 399.75f, 213.246f, 401.746f, 10, 0, 0, +0, 0, 0, 0, 0, 4, 259.25f, 413.75f, 259.25f, 413.75f, +266.609f, 413.664f, 265.246f, 411.746f, 263.234f, 409.129f, 259.25f, 410.75f, 259.25f, 410.75f, +259.25f, 413.75f, 10, 0, 0, 0, 0, 0, 0, 0, +4, 270.25f, 417.75f, 270.25f, 417.75f, 276.855f, 421.832f, 276.246f, 416.746f, 275.973f, +413.453f, 271.25f, 415.75f, 271.25f, 415.75f, 270.25f, 417.75f, 10, 0, 0, +0, 0, 0, 0, 0, 4, 280.246f, 421.75f, 280.242f, 421.75f, +288.223f, 425.367f, 286.246f, 419.75f, 285.457f, 416.664f, 281.25f, 418.746f, 281.25f, 418.746f, +280.246f, 421.75f, 10, 0, 0, 0, 0, 0, 0, 0, +4, 291.246f, 426.75f, 291.242f, 426.75f, 295.605f, 431.996f, 297.246f, 424.75f, 297.227f, +421.875f, 291.246f, 423.746f, 291.246f, 423.746f, 291.246f, 426.75f, 10, 0, 0, +0, 0, 0, 0, 0, 4, 249.25f, 408.75f, 249.25f, 408.75f, +255.266f, 408.652f, 254.246f, 406.75f, 252.73f, 405.242f, 250.25f, 405.746f, 250.25f, 405.746f, +249.25f, 408.75f, 10, 0, 1, 1, 1, 1, 1, 1, +5, 288.246f, 541.746f, 288.246f, 541.742f, 287.875f, 541.203f, 288.246f, 542.75f, 287.875f, +543.559f, 307.109f, 558.148f, 317.25f, 559.746f, 317.25f, 559.746f, 299.125f, 552.27f, 288.246f, +541.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, 5, +288.246f, 541.746f, 288.246f, 541.742f, 287.875f, 541.203f, 288.246f, 542.75f, 287.875f, 543.559f, +307.109f, 558.148f, 317.25f, 559.746f, 317.25f, 559.746f, 299.125f, 552.27f, 288.246f, 541.746f, +10, 0, 0, 0, 0, 0, 0, 0, 10, 292.25f, +471.746f, 292.25f, 471.742f, 316.141f, 447.117f, 326.25f, 442.746f, 326.25f, 442.742f, 336.379f, +430.836f, 332.246f, 401.746f, 332.246f, 401.746f, 328.461f, 393.879f, 325.25f, 416.746f, 325.25f, +416.742f, 328.461f, 444.477f, 316.25f, 426.75f, 316.25f, 426.75f, 306.898f, 437.766f, 314.25f, +437.75f, 314.25f, 437.75f, 317.461f, 435.238f, 318.25f, 436.746f, 318.34f, 438.758f, 309.539f, +453.719f, 290.246f, 469.746f, 271.699f, 485.398f, 292.25f, 471.746f, 292.25f, 471.746f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 227.25f, 428.75f, +227.25f, 428.75f, 227.477f, 431.059f, 229.25f, 429.746f, 231.438f, 429.297f, 335.059f, 422.477f, +370.25f, 395.75f, 370.25f, 395.75f, 320.098f, 421.598f, 227.25f, 428.75f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 227.25f, 428.75f, 227.25f, +428.75f, 227.477f, 431.059f, 229.25f, 429.746f, 231.438f, 429.297f, 335.059f, 422.477f, 370.25f, +395.75f, 370.25f, 395.75f, 320.098f, 421.598f, 227.25f, 428.75f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 239.25f, 433.746f, 239.25f, 433.742f, +238.918f, 435.898f, 241.25f, 434.746f, 242.879f, 434.137f, 393.141f, 435.238f, 419.246f, 399.75f, +419.246f, 399.75f, 394.898f, 427.316f, 239.25f, 433.746f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 239.25f, 433.746f, 239.25f, 433.742f, 238.918f, +435.898f, 241.25f, 434.746f, 242.879f, 434.137f, 393.141f, 435.238f, 419.246f, 399.75f, 419.246f, +399.75f, 394.898f, 427.316f, 239.25f, 433.746f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 252.25f, 438.746f, 252.25f, 438.742f, 251.68f, 440.297f, +253.246f, 439.75f, 255.637f, 438.535f, 446.379f, 452.836f, 472.25f, 416.746f, 472.25f, 416.742f, +461.777f, 445.355f, 252.25f, 438.746f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 252.25f, 438.746f, 252.25f, 438.742f, 251.68f, 440.297f, 253.246f, +439.75f, 255.637f, 438.535f, 446.379f, 452.836f, 472.25f, 416.746f, 472.25f, 416.742f, 461.777f, +445.355f, 252.25f, 438.746f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 263.25f, 443.75f, 263.25f, 443.75f, 262.68f, 445.578f, 264.246f, 444.746f, +266.637f, 443.816f, 401.059f, 486.277f, 427.25f, 450.75f, 427.25f, 450.75f, 412.277f, 477.699f, +263.25f, 443.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 263.25f, 443.75f, 263.25f, 443.75f, 262.68f, 445.578f, 264.246f, 444.746f, 266.637f, +443.816f, 401.059f, 486.277f, 427.25f, 450.75f, 427.25f, 450.75f, 412.277f, 477.699f, 263.25f, +443.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +199.246f, 418.746f, 199.246f, 418.742f, 198.879f, 420.496f, 201.246f, 419.75f, 202.84f, 418.738f, +222.418f, 416.316f, 224.246f, 373.75f, 224.242f, 373.75f, 216.699f, 419.836f, 199.246f, 418.746f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 199.246f, +418.746f, 199.246f, 418.742f, 198.879f, 420.496f, 201.246f, 419.75f, 202.84f, 418.738f, 222.418f, +416.316f, 224.246f, 373.75f, 224.242f, 373.75f, 216.699f, 419.836f, 199.246f, 418.746f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 189.246f, 416.746f, +189.246f, 416.742f, 189.199f, 418.738f, 191.246f, 417.75f, 193.156f, 416.977f, 208.777f, 422.035f, +205.25f, 379.746f, 205.25f, 379.746f, 207.02f, 418.078f, 189.246f, 416.746f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 189.246f, 416.746f, 189.246f, +416.742f, 189.199f, 418.738f, 191.246f, 417.75f, 193.156f, 416.977f, 208.777f, 422.035f, 205.25f, +379.746f, 205.25f, 379.746f, 207.02f, 418.078f, 189.246f, 416.746f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 180.246f, 416.746f, 180.242f, 416.742f, +180.398f, 418.297f, 182.25f, 417.75f, 184.359f, 416.535f, 201.297f, 415.879f, 187.246f, 390.746f, +187.246f, 390.746f, 198.219f, 417.637f, 180.246f, 416.746f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 180.246f, 416.746f, 180.242f, 416.742f, 180.398f, +418.297f, 182.25f, 417.75f, 184.359f, 416.535f, 201.297f, 415.879f, 187.246f, 390.746f, 187.246f, +390.746f, 198.219f, 417.637f, 180.246f, 416.746f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 232.246f, 464.746f, 232.246f, 464.742f, 232.187f, 462.887f, +234.246f, 463.75f, 251.566f, 478.113f, 287.254f, 542.906f, 348.25f, 548.746f, 348.25f, 548.746f, +306.367f, 562.426f, 232.246f, 464.746f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 232.246f, 464.746f, 232.246f, 464.742f, 232.187f, 462.887f, 234.246f, +463.75f, 251.566f, 478.113f, 287.254f, 542.906f, 348.25f, 548.746f, 348.25f, 548.746f, 306.367f, +562.426f, 232.246f, 464.746f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 245.246f, 466.746f, 245.246f, 466.742f, 243.496f, 468.379f, 245.246f, 468.75f, +247.605f, 469.754f, 371.293f, 549.508f, 414.25f, 540.75f, 414.25f, 540.75f, 384.688f, 549.004f, +245.246f, 466.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 245.246f, 466.746f, 245.246f, 466.742f, 243.496f, 468.379f, 245.246f, 468.75f, 247.605f, +469.754f, 371.293f, 549.508f, 414.25f, 540.75f, 414.25f, 540.75f, 384.688f, 549.004f, 245.246f, +466.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +222.246f, 460.746f, 222.246f, 460.742f, 221.512f, 458.594f, 223.246f, 459.75f, 233.266f, 465.301f, +237.242f, 528.234f, 285.25f, 529.75f, 285.25f, 529.75f, 249.523f, 545.801f, 222.246f, 460.746f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 222.246f, +460.746f, 222.246f, 460.742f, 221.512f, 458.594f, 223.246f, 459.75f, 233.266f, 465.301f, 237.242f, +528.234f, 285.25f, 529.75f, 285.25f, 529.75f, 249.523f, 545.801f, 222.246f, 460.746f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 200.246f, 447.746f, +200.246f, 447.746f, 198.973f, 446.812f, 201.246f, 446.75f, 212.391f, 448.555f, 235.937f, 493.953f, +285.25f, 488.746f, 285.25f, 488.742f, 249.656f, 504.148f, 200.246f, 447.746f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 200.246f, 447.746f, 200.246f, +447.746f, 198.973f, 446.812f, 201.246f, 446.75f, 212.391f, 448.555f, 235.937f, 493.953f, 285.25f, +488.746f, 285.25f, 488.742f, 249.656f, 504.148f, 200.246f, 447.746f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 212.246f, 454.75f, 212.246f, 454.75f, +211.625f, 453.348f, 213.246f, 453.746f, 224.461f, 457.637f, 238.852f, 506.711f, 288.246f, 510.746f, +288.246f, 510.742f, 250.363f, 519.348f, 212.246f, 454.75f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 212.246f, 454.75f, 212.246f, 454.75f, 211.625f, +453.348f, 213.246f, 453.746f, 224.461f, 457.637f, 238.852f, 506.711f, 288.246f, 510.746f, 288.246f, +510.742f, 250.363f, 519.348f, 212.246f, 454.75f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 192.25f, 442.746f, 192.25f, 442.742f, 191.453f, 441.449f, +193.25f, 441.75f, 202.32f, 442.863f, 221.395f, 479.633f, 261.25f, 474.75f, 261.25f, 474.75f, +232.508f, 487.891f, 192.25f, 442.746f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 192.25f, 442.746f, 192.25f, 442.742f, 191.453f, 441.449f, 193.25f, +441.75f, 202.32f, 442.863f, 221.395f, 479.633f, 261.25f, 474.75f, 261.25f, 474.75f, 232.508f, +487.891f, 192.25f, 442.746f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 184.25f, 435.75f, 184.25f, 435.75f, 182.949f, 434.945f, 184.25f, 434.746f, +189.281f, 435.414f, 222.984f, 471.801f, 243.246f, 454.75f, 243.246f, 454.75f, 230.082f, 475.344f, +184.25f, 435.75f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 184.25f, 435.75f, 184.25f, 435.75f, 182.949f, 434.945f, 184.25f, 434.746f, 189.281f, +435.414f, 222.984f, 471.801f, 243.246f, 454.75f, 243.246f, 454.75f, 230.082f, 475.344f, 184.25f, +435.75f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +260.25f, 470.75f, 260.25f, 470.75f, 259.219f, 472.699f, 261.25f, 472.75f, 263.469f, 473.547f, +396.242f, 537.031f, 438.25f, 522.746f, 438.25f, 522.746f, 409.465f, 534.84f, 260.25f, 470.75f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 260.25f, +470.75f, 260.25f, 470.75f, 259.219f, 472.699f, 261.25f, 472.75f, 263.469f, 473.547f, 396.242f, +537.031f, 438.25f, 522.746f, 438.25f, 522.746f, 409.465f, 534.84f, 260.25f, 470.75f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 223.246f, 400.75f, +223.246f, 400.75f, 223.52f, 402.457f, 225.25f, 401.746f, 227.477f, 400.695f, 244.418f, 400.035f, +231.246f, 375.75f, 231.246f, 375.75f, 241.34f, 401.797f, 223.246f, 400.75f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 223.246f, 400.75f, 223.246f, +400.75f, 223.52f, 402.457f, 225.25f, 401.746f, 227.477f, 400.695f, 244.418f, 400.035f, 231.246f, +375.75f, 231.246f, 375.75f, 241.34f, 401.797f, 223.246f, 400.75f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 235.246f, 401.746f, 235.242f, 401.746f, +234.957f, 404.219f, 237.25f, 403.746f, 238.918f, 402.457f, 258.5f, 400.035f, 260.25f, 357.746f, +260.25f, 357.746f, 252.777f, 403.559f, 235.246f, 401.746f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 235.246f, 401.746f, 235.242f, 401.746f, 234.957f, +404.219f, 237.25f, 403.746f, 238.918f, 402.457f, 258.5f, 400.035f, 260.25f, 357.746f, 260.25f, +357.746f, 252.777f, 403.559f, 235.246f, 401.746f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 242.246f, 403.746f, 242.246f, 403.746f, 242.437f, 405.977f, +244.246f, 404.75f, 246.398f, 404.219f, 273.457f, 400.477f, 299.246f, 364.75f, 299.246f, 364.75f, +260.258f, 405.316f, 242.246f, 403.746f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 242.246f, 403.746f, 242.246f, 403.746f, 242.437f, 405.977f, 244.246f, +404.75f, 246.398f, 404.219f, 273.457f, 400.477f, 299.246f, 364.75f, 299.246f, 364.75f, 260.258f, +405.316f, 242.246f, 403.746f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 251.25f, 405.746f, 251.25f, 405.742f, 250.566f, 408.164f, 252.25f, 407.746f, +254.723f, 406.945f, 277.199f, 409.031f, 319.25f, 371.75f, 319.25f, 371.75f, 268.316f, 409.875f, +251.25f, 405.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 251.25f, 405.746f, 251.25f, 405.742f, 250.566f, 408.164f, 252.25f, 407.746f, 254.723f, +406.945f, 277.199f, 409.031f, 319.25f, 371.75f, 319.25f, 371.75f, 268.316f, 409.875f, 251.25f, +405.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +262.25f, 410.75f, 262.25f, 410.75f, 262.004f, 413.004f, 264.246f, 412.746f, 266.164f, 411.785f, +304.48f, 406.832f, 361.25f, 368.746f, 361.25f, 368.746f, 279.754f, 414.715f, 262.25f, 410.75f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 262.25f, +410.75f, 262.25f, 410.75f, 262.004f, 413.004f, 264.246f, 412.746f, 266.164f, 411.785f, 304.48f, +406.832f, 361.25f, 368.746f, 361.25f, 368.746f, 279.754f, 414.715f, 262.25f, 410.75f, 10, +0, 1, 1, 1, 1, 1, 1, 5, 218.25f, 423.746f, +218.25f, 423.746f, 217.797f, 425.777f, 220.246f, 424.75f, 221.758f, 424.016f, 280.5f, 421.156f, +314.25f, 391.75f, 314.25f, 391.75f, 275.547f, 418.93f, 218.25f, 423.746f, 10, 0.11f, +0, 0, 0, 0, 0, 0, 5, 218.25f, 423.746f, 218.25f, +423.746f, 217.797f, 425.777f, 220.246f, 424.75f, 221.758f, 424.016f, 280.5f, 421.156f, 314.25f, +391.75f, 314.25f, 391.75f, 275.547f, 418.93f, 218.25f, 423.746f, 10, 0, 1, +1, 1, 1, 1, 1, 5, 207.25f, 419.75f, 207.25f, 419.75f, +206.797f, 421.379f, 209.246f, 420.746f, 210.758f, 419.617f, 237.816f, 415.879f, 264.246f, 379.746f, +264.246f, 379.746f, 224.617f, 420.719f, 207.25f, 419.75f, 10, 0.11f, 0, 0, +0, 0, 0, 0, 5, 207.25f, 419.75f, 207.25f, 419.75f, 206.797f, +421.379f, 209.246f, 420.746f, 210.758f, 419.617f, 237.816f, 415.879f, 264.246f, 379.746f, 264.246f, +379.746f, 224.617f, 420.719f, 207.25f, 419.75f, 10, 0, 1, 1, 1, +1, 1, 1, 5, 274.25f, 415.75f, 274.25f, 415.75f, 273.828f, 418.031f, +276.246f, 417.75f, 278.066f, 417.125f, 316.645f, 414.992f, 376.246f, 380.75f, 376.246f, 380.75f, +290.746f, 418.625f, 274.25f, 415.75f, 10, 0.11f, 0, 0, 0, 0, +0, 0, 5, 274.25f, 415.75f, 274.25f, 415.75f, 273.828f, 418.031f, 276.246f, +417.75f, 278.066f, 417.125f, 316.645f, 414.992f, 376.246f, 380.75f, 376.246f, 380.75f, 290.746f, +418.625f, 274.25f, 415.75f, 10, 0, 1, 1, 1, 1, 1, +1, 5, 283.25f, 418.746f, 283.25f, 418.742f, 283.07f, 420.672f, 285.25f, 419.75f, +287.309f, 419.762f, 325.883f, 417.633f, 385.25f, 383.746f, 385.25f, 383.742f, 300.648f, 421.703f, +283.25f, 418.746f, 10, 0.11f, 0, 0, 0, 0, 0, 0, +5, 283.25f, 418.746f, 283.25f, 418.742f, 283.07f, 420.672f, 285.25f, 419.75f, 287.309f, +419.762f, 325.883f, 417.633f, 385.25f, 383.746f, 385.25f, 383.742f, 300.648f, 421.703f, 283.25f, +418.746f, 10, 0, 1, 1, 1, 1, 1, 1, 5, +294.25f, 424.75f, 294.25f, 424.75f, 293.629f, 426.172f, 296.25f, 425.746f, 297.867f, 425.262f, +345.242f, 420.492f, 444.246f, 382.75f, 444.246f, 382.75f, 311.207f, 427.203f, 294.25f, 424.75f, +10, 0.11f, 0, 0, 0, 0, 0, 0, 5, 294.25f, +424.75f, 294.25f, 424.75f, 293.629f, 426.172f, 296.25f, 425.746f, 297.867f, 425.262f, 345.242f, +420.492f, 444.246f, 382.75f, 444.246f, 382.75f, 311.207f, 427.203f, 294.25f, 424.75f, 10, +0, 0, 0, 0, 0, 0, 0, 4, 172.25f, 416.746f, +172.25f, 416.742f, 177.539f, 417.195f, 176.246f, 415.75f, 174.898f, 413.676f, 172.25f, 414.746f, +172.25f, 414.746f, 172.25f, 416.746f, 10, 0, 0, 0, 0, 0, +0, 0, 4, 205.25f, 401.746f, 205.25f, 401.746f, 211.418f, 401.797f, 210.246f, +399.75f, 208.777f, 398.277f, 206.25f, 398.746f, 206.25f, 398.746f, 205.25f, 401.746f, 10, +0, 0, 0, 0, 0, 0, 0, 4, 196.25f, 401.746f, +196.25f, 401.746f, 201.738f, 402.238f, 200.246f, 400.75f, 199.098f, 398.719f, 196.25f, 399.75f, +196.25f, 399.75f, 196.25f, 401.746f, 10, 0, 0, 0, 0, 0, +0, 0, 4, 91.25f, 414.746f, 91.25f, 414.746f, 96.6602f, 413.344f, 95.25f, +411.746f, 93.0156f, 410.879f, 91.25f, 412.746f, 91.25f, 412.746f, 91.25f, 414.746f, 10, +0, 0, 0, 0, 0, 0, 0, 4, 93.2461f, 425.746f, +93.2422f, 425.746f, 98.8633f, 423.902f, 97.25f, 422.746f, 95.2148f, 421.441f, 93.2461f, 422.746f, +93.2461f, 422.746f, 93.2461f, 425.746f, 10, 0, 0, 0, 0, 0, +0, 0, 4, 85.25f, 429.746f, 85.25f, 429.742f, 90.9414f, 428.742f, 89.2461f, +427.746f, 87.2969f, 426.281f, 85.25f, 427.746f, 85.25f, 427.746f, 85.25f, 429.746f, 10, +0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 173.25f, 347.75f, +173.25f, 347.75f, 171.379f, 347.676f, 167.25f, 345.75f, 164.777f, 345.477f, 152.457f, 341.516f, +146.25f, 330.746f, 146.25f, 330.742f, 159.938f, 341.078f, 173.25f, 347.75f, 10, 0, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 9, 269.246f, 245.746f, 269.781f, +245.484f, 269.84f, 245.02f, 270.25f, 244.75f, 270.887f, 244.957f, 272.242f, 244.625f, 272.25f, +245.746f, 271.172f, 250.063f, 270.211f, 255.492f, 265.246f, 257.746f, 264.961f, 257.789f, 263.375f, +257.332f, 263.25f, 256.746f, 263.156f, 254.684f, 263.027f, 253.203f, 263.25f, 251.75f, 263.695f, +250.027f, 266.07f, 250.016f, 267.246f, 251.75f, 268.109f, 249.699f, 268.582f, 247.672f, 269.246f, +245.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 9, +257.246f, 240.75f, 258.262f, 239.004f, 258.121f, 236.961f, 259.25f, 236.746f, 260.492f, 236.016f, +262.527f, 237.09f, 262.25f, 238.746f, 261.188f, 240.539f, 260.758f, 243, 259.25f, 244.75f, +259.012f, 245.281f, 259.277f, 245.867f, 259.25f, 245.746f, 258.445f, 247.57f, 257.188f, 248.379f, +255.246f, 247.746f, 254.41f, 245.594f, 255.676f, 243.25f, 257.246f, 241.75f, 257.5f, 241.203f, +257.316f, 240.793f, 257.246f, 240.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 11, 214.25f, 246.746f, 213.758f, 246.684f, 213.719f, 247.191f, 214.25f, +247.746f, 214.484f, 248.68f, 215.355f, 249.914f, 215.25f, 250.746f, 214.602f, 252.203f, 213.375f, +252, 212.246f, 251.75f, 211.41f, 250.281f, 211.355f, 248.273f, 210.246f, 246.746f, 210.379f, +246.355f, 210.438f, 245.723f, 210.246f, 245.746f, 209.43f, 244.832f, 208.945f, 243.152f, 209.246f, +242.75f, 209.109f, 242.18f, 208.91f, 231.281f, 209.246f, 231.75f, 209.836f, 232.379f, 213.188f, +243.086f, 213.246f, 243.75f, 213.328f, 244.871f, 214.133f, 245.383f, 214.25f, 246.746f, 10, +0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 7, 185.25f, 253.75f, +188.574f, 256.488f, 191.641f, 259.746f, 191.246f, 263.75f, 191.027f, 264.902f, 189.074f, 264.32f, +189.246f, 263.75f, 187.988f, 259.402f, 185.746f, 256.477f, 183.25f, 253.75f, 180.504f, 251.594f, +178.457f, 244.617f, 178.246f, 243.75f, 182.266f, 249.84f, 184.746f, 252.859f, 185.25f, 253.75f, +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 10, 170.246f, +260.746f, 171.32f, 260.707f, 170.988f, 261.246f, 171.246f, 261.746f, 172.273f, 263.215f, 173.707f, +264.586f, 173.25f, 266.746f, 173.73f, 266.805f, 173.313f, 267.145f, 173.25f, 266.746f, 172.641f, +266.695f, 172.262f, 266.551f, 172.25f, 266.746f, 169.91f, 263.715f, 168.371f, 260.777f, 167.25f, +257.746f, 166.582f, 257.289f, 165.324f, 252.352f, 165.246f, 251.75f, 165.934f, 252.133f, 167.824f, +256.734f, 168.25f, 256.746f, 169.445f, 257.613f, 169.457f, 259.391f, 170.246f, 260.746f, 10, +0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 189.246f, 238.746f, +189.641f, 239.758f, 191.371f, 241.078f, 191.246f, 241.75f, 191.117f, 243.078f, 191.633f, 244.664f, +190.246f, 243.75f, 189.246f, 242.867f, 185.453f, 241.383f, 185.25f, 234.746f, 185.129f, 234.363f, +188.398f, 237.328f, 189.246f, 238.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 19, 205.25f, 257.746f, 205.477f, 258.434f, 206.258f, 257.91f, 207.25f, +257.746f, 207.473f, 258.609f, 208.148f, 259.223f, 208.25f, 259.746f, 209.535f, 262.301f, 211.48f, +264.305f, 211.246f, 266.746f, 209.996f, 268.484f, 209.25f, 266.238f, 208.25f, 264.746f, 207.102f, +266.988f, 206.004f, 264.926f, 204.25f, 264.746f, 204.496f, 264.324f, 204.262f, 264.707f, 204.25f, +264.746f, 202.887f, 264.195f, 202.137f, 263.004f, 201.246f, 261.746f, 200.852f, 261.996f, 200.406f, +262.195f, 200.246f, 261.746f, 199.527f, 261.383f, 198.457f, 261.027f, 198.246f, 260.746f, 196.93f, +257.297f, 193.473f, 254.992f, 191.246f, 246.746f, 191.816f, 245.695f, 196.359f, 254.004f, 197.25f, +254.75f, 197.816f, 256.086f, 197.945f, 252.945f, 199.246f, 253.75f, 199.406f, 253.707f, 199.609f, +253.445f, 200.246f, 253.75f, 199.973f, 253.605f, 200.211f, 253.855f, 200.246f, 253.75f, 200.637f, +254.176f, 200.492f, 254.789f, 200.246f, 254.75f, 202.074f, 256.039f, 201.98f, 257.215f, 203.25f, +258.746f, 203.344f, 257.711f, 204.508f, 258.5f, 205.25f, 257.746f, 10, 0, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 271.25f, 197.75f, 271.25f, 197.75f, +278.957f, 220.297f, 274.25f, 232.75f, 274.25f, 232.75f, 286.656f, 208.855f, 281.25f, 196.75f, +281.25f, 196.75f, 281.156f, 207.977f, 277.246f, 213.746f, 277.246f, 213.746f, 272.359f, 199.398f, +271.25f, 197.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +5, 254.246f, 200.75f, 254.246f, 200.75f, 260.477f, 210.398f, 251.25f, 230.75f, 251.25f, +230.75f, 250.797f, 208.195f, 243.246f, 195.746f, 243.246f, 195.742f, 258.937f, 218.316f, 254.246f, +200.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, +243.246f, 202.746f, 243.246f, 202.746f, 243.316f, 224.918f, 244.246f, 227.746f, 244.246f, 227.742f, +239.578f, 209.957f, 228.25f, 199.75f, 228.25f, 199.75f, 244.199f, 212.598f, 243.246f, 202.746f, +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, 233.246f, +237.746f, 233.246f, 237.746f, 239.578f, 223.156f, 228.25f, 202.746f, 228.25f, 202.746f, 235.617f, +216.336f, 230.25f, 223.746f, 230.25f, 223.746f, 233.199f, 227.777f, 233.246f, 237.746f, 10, +0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 8, 212.246f, 203.746f, +212.246f, 203.746f, 210.758f, 220.516f, 212.246f, 222.75f, 212.246f, 222.75f, 212.957f, 229.977f, +212.246f, 230.75f, 212.246f, 230.75f, 216.918f, 237.898f, 217.25f, 229.75f, 217.25f, 229.75f, +218.68f, 221.176f, 222.246f, 215.746f, 222.246f, 215.746f, 225.719f, 210.176f, 225.25f, 202.746f, +225.25f, 202.746f, 214.5f, 236.355f, 212.246f, 203.746f, 10, 0, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 0.8f, 5, 208.25f, 233.75f, 208.25f, 233.75f, 200.637f, +221.836f, 198.246f, 200.75f, 198.246f, 200.75f, 197.117f, 207.758f, 201.246f, 223.746f, 201.246f, +223.746f, 205.918f, 240.535f, 208.25f, 233.75f, 10, 0, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 6, 184.25f, 211.75f, 184.25f, 211.75f, 189.418f, 217.879f, +191.246f, 223.746f, 191.242f, 223.746f, 194.918f, 240.758f, 188.246f, 231.75f, 188.246f, 231.75f, +188.098f, 222.496f, 179.246f, 214.746f, 179.246f, 214.746f, 184.359f, 216.996f, 184.25f, 211.75f, +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 177.246f, +217.746f, 177.246f, 217.742f, 181.277f, 236.578f, 182.25f, 237.746f, 182.25f, 237.746f, 184.137f, +241.195f, 181.25f, 237.746f, 181.25f, 237.746f, 171.379f, 216.559f, 167.25f, 209.75f, 167.25f, +209.75f, 175.777f, 219.418f, 177.246f, 217.746f, 10, 0, 0.8f, 0.8f, 0.8f, +0.8f, 0.8f, 0.8f, 4, 171.246f, 235.746f, 171.246f, 235.746f, 183.918f, 260.336f, +160.246f, 231.75f, 160.246f, 231.75f, 172.039f, 242.738f, 171.246f, 235.746f, 10, 0, +0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 6, 154.246f, 251.75f, 154.242f, +251.75f, 159.5f, 272.438f, 162.25f, 271.746f, 162.25f, 271.746f, 171.379f, 282.117f, 164.246f, +270.75f, 164.242f, 270.75f, 157.52f, 259.898f, 158.25f, 248.746f, 158.25f, 248.746f, 157.52f, +259.676f, 154.246f, 251.75f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +0.8f, 5, 517.246f, 264.746f, 517.242f, 264.742f, 504.348f, 275.297f, 501.25f, 278.75f, +501.25f, 278.75f, 516.449f, 258.797f, 516.25f, 250.746f, 516.25f, 250.742f, 519.199f, 259.348f, +517.246f, 264.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, +5, 523.25f, 288.746f, 523.25f, 288.742f, 500.5f, 305, 496.25f, 312.75f, 496.25f, +312.75f, 525.797f, 280.797f, 526.246f, 275.746f, 526.242f, 275.742f, 526.348f, 285.75f, 523.25f, +288.746f, 10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 5, +542.246f, 457.75f, 542.246f, 457.75f, 529.098f, 466.699f, 527.25f, 464.746f, 527.25f, 464.742f, +539, 457.348f, 542.246f, 447.746f, 542.246f, 447.746f, 540.098f, 457.898f, 542.246f, 457.75f, +10, 0, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 0.8f, 4, 551.25f, +369.75f, 532.25f, 382.75f, 532.25f, 382.75f, 553.297f, 363.848f, 554.25f, 359.746f, 551.25f, +369.75f, 10, 1.1f, 0, 0, 0, 0, 0, 0, 2, +122.246f, 393.75f, 146.25f, 388.75f, 10, 1.1f, 0, 0, 0, 0, +0, 0, 2, 177.246f, 215.746f, 177.246f, 215.746f, 176.547f, 219.75f, 166.246f, +207.75f, 10, 1.1f, 0, 0, 0, 0, 0, 0, 2, +183.25f, 210.75f, 183.25f, 210.75f, 185.348f, 217.547f, 178.246f, 212.746f, 10, 1.1f, +0, 0, 0, 0, 0, 0, 2, 242.246f, 200.75f, 242.246f, +200.75f, 244.199f, 213.148f, 231.246f, 198.75f}; + diff --git a/host_applications/linux/apps/hello_pi/hello_tiger/tiger.h b/host_applications/linux/apps/hello_pi/hello_tiger/tiger.h new file mode 100755 index 0000000..c83d3f1 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_tiger/tiger.h @@ -0,0 +1,45 @@ +#ifndef __TIGER_H +#define __TIGER_H + +/*------------------------------------------------------------------------ + * + * OpenVG 1.0.1 Reference Implementation sample code + * ------------------------------------------------- + * + * Copyright (c) 2007 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and /or associated documentation files + * (the "Materials "), to deal in the Materials without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Materials, + * and to permit persons to whom the Materials are 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 Materials. + * + * THE MATERIALS ARE 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 MATERIALS OR + * THE USE OR OTHER DEALINGS IN THE MATERIALS. + * + *//** + * \file + * \brief Header for including the Tiger image data. + * \note + *//*-------------------------------------------------------------------*/ + +extern const int tigerCommandCount; +extern const char tigerCommands[]; +extern const float tigerMinX; +extern const float tigerMaxX; +extern const float tigerMinY; +extern const float tigerMaxY; +extern const int tigerPointCount; +extern const float tigerPoints[]; + +#endif /* __TIGER_H */ diff --git a/host_applications/linux/apps/hello_pi/hello_triangle/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_triangle/CMakeLists.txt new file mode 100755 index 0000000..4e8128e --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_triangle.bin) +set(SRCS triangle.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_triangle/Makefile b/host_applications/linux/apps/hello_pi/hello_triangle/Makefile new file mode 100755 index 0000000..45dcc0d --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle/Makefile @@ -0,0 +1,5 @@ +OBJS=triangle.o +BIN=hello_triangle.bin + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_triangle/cube_texture_and_coords.h b/host_applications/linux/apps/hello_pi/hello_triangle/cube_texture_and_coords.h new file mode 100755 index 0000000..7dd30a9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle/cube_texture_and_coords.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Spatial coordinates for the cube + +static const GLbyte quadx[6*4*3] = { + /* FRONT */ + -10, -10, 10, + 10, -10, 10, + -10, 10, 10, + 10, 10, 10, + + /* BACK */ + -10, -10, -10, + -10, 10, -10, + 10, -10, -10, + 10, 10, -10, + + /* LEFT */ + -10, -10, 10, + -10, 10, 10, + -10, -10, -10, + -10, 10, -10, + + /* RIGHT */ + 10, -10, -10, + 10, 10, -10, + 10, -10, 10, + 10, 10, 10, + + /* TOP */ + -10, 10, 10, + 10, 10, 10, + -10, 10, -10, + 10, 10, -10, + + /* BOTTOM */ + -10, -10, 10, + -10, -10, -10, + 10, -10, 10, + 10, -10, -10, +}; + +/** Texture coordinates for the quad. */ +static const GLfloat texCoords[6 * 4 * 2] = { + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f +}; + diff --git a/host_applications/linux/apps/hello_pi/hello_triangle/triangle.c b/host_applications/linux/apps/hello_pi/hello_triangle/triangle.c new file mode 100755 index 0000000..0956c97 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle/triangle.c @@ -0,0 +1,551 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// A rotating cube rendered with OpenGL|ES. Three images used as textures on the cube faces. + +#include +#include +#include +#include +#include +#include + +#include "bcm_host.h" + +#include "GLES/gl.h" +#include "EGL/egl.h" +#include "EGL/eglext.h" + +#include "cube_texture_and_coords.h" + +#define PATH "./" + +#define IMAGE_SIZE 128 + +#ifndef M_PI + #define M_PI 3.141592654 +#endif + + +typedef struct +{ + uint32_t screen_width; + uint32_t screen_height; +// OpenGL|ES objects + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_ELEMENT_HANDLE_T dispman_element; + EGLDisplay display; + EGLSurface surface; + EGLContext context; + GLuint tex[6]; +// model rotation vector and direction + GLfloat rot_angle_x_inc; + GLfloat rot_angle_y_inc; + GLfloat rot_angle_z_inc; +// current model rotation angles + GLfloat rot_angle_x; + GLfloat rot_angle_y; + GLfloat rot_angle_z; +// current distance from camera + GLfloat distance; + GLfloat distance_inc; +// pointers to texture buffers + char *tex_buf1; + char *tex_buf2; + char *tex_buf3; +} CUBE_STATE_T; + +static void init_ogl(CUBE_STATE_T *state); +static void init_model_proj(CUBE_STATE_T *state); +static void reset_model(CUBE_STATE_T *state); +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); +static void redraw_scene(CUBE_STATE_T *state); +static void update_model(CUBE_STATE_T *state); +static void init_textures(CUBE_STATE_T *state); +static void load_tex_images(CUBE_STATE_T *state); +static void exit_func(void); +static volatile int terminate; +static CUBE_STATE_T _state, *state=&_state; + + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void init_ogl(CUBE_STATE_T *state) +{ + int32_t success = 0; + EGLBoolean result; + EGLint num_config; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + EGLConfig config; + + // get an EGL display connection + state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + assert(state->display!=EGL_NO_DISPLAY); + + // initialize the EGL display connection + result = eglInitialize(state->display, NULL, NULL); + assert(EGL_FALSE != result); + + // get an appropriate EGL frame buffer configuration + result = eglChooseConfig(state->display, attribute_list, &config, 1, &num_config); + assert(EGL_FALSE != result); + + // create an EGL rendering context + state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); + assert(state->context!=EGL_NO_CONTEXT); + + // create an EGL window surface + success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); + assert( success >= 0 ); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = state->screen_width; + dst_rect.height = state->screen_height; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = state->screen_width << 16; + src_rect.height = state->screen_height << 16; + + state->dispman_display = vc_dispmanx_display_open( 0 /* LCD */); + dispman_update = vc_dispmanx_update_start( 0 ); + + state->dispman_element = vc_dispmanx_element_add ( dispman_update, state->dispman_display, + 0/*layer*/, &dst_rect, 0/*src*/, + &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); + + nativewindow.element = state->dispman_element; + nativewindow.width = state->screen_width; + nativewindow.height = state->screen_height; + vc_dispmanx_update_submit_sync( dispman_update ); + + state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, NULL ); + assert(state->surface != EGL_NO_SURFACE); + + // connect the context to the surface + result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); + assert(EGL_FALSE != result); + + // Set background color and clear buffers + glClearColor(0.15f, 0.25f, 0.35f, 1.0f); + + // Enable back face culling. + glEnable(GL_CULL_FACE); + + glMatrixMode(GL_MODELVIEW); +} + +/*********************************************************** + * Name: init_model_proj + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the OpenGL|ES model to default values + * + * Returns: void + * + ***********************************************************/ +static void init_model_proj(CUBE_STATE_T *state) +{ + float nearp = 1.0f; + float farp = 500.0f; + float hht; + float hwd; + + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + + glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); + hwd = hht * (float)state->screen_width / (float)state->screen_height; + + glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); + + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( 3, GL_BYTE, 0, quadx ); + + reset_model(state); +} + +/*********************************************************** + * Name: reset_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Resets the Model projection and rotation direction + * + * Returns: void + * + ***********************************************************/ +static void reset_model(CUBE_STATE_T *state) +{ + // reset model position + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.f, 0.f, -50.f); + + // reset model rotation + state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; + state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; + state->distance = 40.f; +} + +/*********************************************************** + * Name: update_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Updates model projection to current position/rotation + * + * Returns: void + * + ***********************************************************/ +static void update_model(CUBE_STATE_T *state) +{ + // update position + state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); + state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); + state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); + state->distance = inc_and_clip_distance(state->distance, state->distance_inc); + + glLoadIdentity(); + // move camera back to see the cube + glTranslatef(0.f, 0.f, -state->distance); + + // Rotate model to new position + glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); + glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); + glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); +} + +/*********************************************************** + * Name: inc_and_wrap_angle + * + * Arguments: + * GLfloat angle current angle + * GLfloat angle_inc angle increment + * + * Description: Increments or decrements angle by angle_inc degrees + * Wraps to 0 at 360 deg. + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) +{ + angle += angle_inc; + + if (angle >= 360.0) + angle -= 360.f; + else if (angle <=0) + angle += 360.f; + + return angle; +} + +/*********************************************************** + * Name: inc_and_clip_distance + * + * Arguments: + * GLfloat distance current distance + * GLfloat distance_inc distance increment + * + * Description: Increments or decrements distance by distance_inc units + * Clips to range + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) +{ + distance += distance_inc; + + if (distance >= 120.0f) + distance = 120.f; + else if (distance <= 40.0f) + distance = 40.0f; + + return distance; +} + +/*********************************************************** + * Name: redraw_scene + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Draws the model and calls eglSwapBuffers + * to render to screen + * + * Returns: void + * + ***********************************************************/ +static void redraw_scene(CUBE_STATE_T *state) +{ + // Start with a clear screen + glClear( GL_COLOR_BUFFER_BIT ); + + // Draw first (front) face: + // Bind texture surface to current vertices + glBindTexture(GL_TEXTURE_2D, state->tex[0]); + + // Need to rotate textures - do this by rotating each cube face + glRotatef(270.f, 0.f, 0.f, 1.f ); // front face normal along z axis + + // draw first 4 vertices + glDrawArrays( GL_TRIANGLE_STRIP, 0, 4); + + // same pattern for other 5 faces - rotation chosen to make image orientation 'nice' + glBindTexture(GL_TEXTURE_2D, state->tex[1]); + glRotatef(90.f, 0.f, 0.f, 1.f ); // back face normal along z axis + glDrawArrays( GL_TRIANGLE_STRIP, 4, 4); + + glBindTexture(GL_TEXTURE_2D, state->tex[2]); + glRotatef(90.f, 1.f, 0.f, 0.f ); // left face normal along x axis + glDrawArrays( GL_TRIANGLE_STRIP, 8, 4); + + glBindTexture(GL_TEXTURE_2D, state->tex[3]); + glRotatef(90.f, 1.f, 0.f, 0.f ); // right face normal along x axis + glDrawArrays( GL_TRIANGLE_STRIP, 12, 4); + + glBindTexture(GL_TEXTURE_2D, state->tex[4]); + glRotatef(270.f, 0.f, 1.f, 0.f ); // top face normal along y axis + glDrawArrays( GL_TRIANGLE_STRIP, 16, 4); + + glBindTexture(GL_TEXTURE_2D, state->tex[5]); + glRotatef(90.f, 0.f, 1.f, 0.f ); // bottom face normal along y axis + glDrawArrays( GL_TRIANGLE_STRIP, 20, 4); + + eglSwapBuffers(state->display, state->surface); +} + +/*********************************************************** + * Name: init_textures + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Initialise OGL|ES texture surfaces to use image + * buffers + * + * Returns: void + * + ***********************************************************/ +static void init_textures(CUBE_STATE_T *state) +{ + // load three texture buffers but use them on six OGL|ES texture surfaces + load_tex_images(state); + glGenTextures(6, &state->tex[0]); + + // setup first texture + glBindTexture(GL_TEXTURE_2D, state->tex[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf1); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + // setup second texture - reuse first image + glBindTexture(GL_TEXTURE_2D, state->tex[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf1); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + // third texture + glBindTexture(GL_TEXTURE_2D, state->tex[2]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf2); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + // fourth texture - reuse second image + glBindTexture(GL_TEXTURE_2D, state->tex[3]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf2); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + //fifth texture + glBindTexture(GL_TEXTURE_2D, state->tex[4]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf3); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + // sixth texture - reuse third image + glBindTexture(GL_TEXTURE_2D, state->tex[5]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, IMAGE_SIZE, IMAGE_SIZE, 0, + GL_RGB, GL_UNSIGNED_BYTE, state->tex_buf3); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)GL_NEAREST); + + // setup overall texture environment + glTexCoordPointer(2, GL_FLOAT, 0, texCoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_TEXTURE_2D); +} + +/*********************************************************** + * Name: load_tex_images + * + * Arguments: + * void + * + * Description: Loads three raw images to use as textures on faces + * + * Returns: void + * + ***********************************************************/ +static void load_tex_images(CUBE_STATE_T *state) +{ + FILE *tex_file1 = NULL, *tex_file2=NULL, *tex_file3 = NULL; + int bytes_read, image_sz = IMAGE_SIZE*IMAGE_SIZE*3; + + state->tex_buf1 = malloc(image_sz); + state->tex_buf2 = malloc(image_sz); + state->tex_buf3 = malloc(image_sz); + + tex_file1 = fopen(PATH "Lucca_128_128.raw", "rb"); + if (tex_file1 && state->tex_buf1) + { + bytes_read=fread(state->tex_buf1, 1, image_sz, tex_file1); + assert(bytes_read == image_sz); // some problem with file? + fclose(tex_file1); + } + + tex_file2 = fopen(PATH "Djenne_128_128.raw", "rb"); + if (tex_file2 && state->tex_buf2) + { + bytes_read=fread(state->tex_buf2, 1, image_sz, tex_file2); + assert(bytes_read == image_sz); // some problem with file? + fclose(tex_file2); + } + + tex_file3 = fopen(PATH "Gaudi_128_128.raw", "rb"); + if (tex_file3 && state->tex_buf3) + { + bytes_read=fread(state->tex_buf3, 1, image_sz, tex_file3); + assert(bytes_read == image_sz); // some problem with file? + fclose(tex_file3); + } +} + +//------------------------------------------------------------------------------ + +static void exit_func(void) +// Function to be passed to atexit(). +{ + DISPMANX_UPDATE_HANDLE_T dispman_update; + int s; + // clear screen + glClear( GL_COLOR_BUFFER_BIT ); + eglSwapBuffers(state->display, state->surface); + + glDeleteTextures(6, state->tex); + eglDestroySurface( state->display, state->surface ); + + dispman_update = vc_dispmanx_update_start( 0 ); + s = vc_dispmanx_element_remove(dispman_update, state->dispman_element); + assert(s == 0); + vc_dispmanx_update_submit_sync( dispman_update ); + s = vc_dispmanx_display_close(state->dispman_display); + assert (s == 0); + + // Release OpenGL resources + eglMakeCurrent( state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + eglDestroyContext( state->display, state->context ); + eglTerminate( state->display ); + + // release texture buffers + free(state->tex_buf1); + free(state->tex_buf2); + free(state->tex_buf3); + + printf("\ncube closed\n"); +} // exit_func() + +//============================================================================== + +int main () +{ + bcm_host_init(); + + // Clear application state + memset( state, 0, sizeof( *state ) ); + + // Start OGLES + init_ogl(state); + + // Setup the model world + init_model_proj(state); + + // initialise the OGLES texture(s) + init_textures(state); + + while (!terminate) + { + update_model(state); + redraw_scene(state); + } + exit_func(); + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_triangle2/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_triangle2/CMakeLists.txt new file mode 100755 index 0000000..390980a --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle2/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_triangle2.bin) +set(SRCS triangle2.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_triangle2/Makefile b/host_applications/linux/apps/hello_pi/hello_triangle2/Makefile new file mode 100755 index 0000000..13b1b39 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle2/Makefile @@ -0,0 +1,5 @@ +OBJS=triangle2.o +BIN=hello_triangle2.bin + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_triangle2/triangle2.c b/host_applications/linux/apps/hello_pi/hello_triangle2/triangle2.c new file mode 100755 index 0000000..bc68c51 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_triangle2/triangle2.c @@ -0,0 +1,509 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// OpenGL|ES 2 demo using shader to compute mandelbrot/julia sets +// Thanks to Peter de Rivas for original Python code + +#include +#include +#include +#include +#include +#include +#include + +#include "bcm_host.h" + +#include "GLES2/gl2.h" +#include "EGL/egl.h" +#include "EGL/eglext.h" + +typedef struct +{ + uint32_t screen_width; + uint32_t screen_height; +// OpenGL|ES objects + EGLDisplay display; + EGLSurface surface; + EGLContext context; + + GLuint verbose; + GLuint vshader; + GLuint fshader; + GLuint mshader; + GLuint program; + GLuint program2; + GLuint tex_fb; + GLuint tex; + GLuint buf; +// julia attribs + GLuint unif_color, attr_vertex, unif_scale, unif_offset, unif_tex, unif_centre; +// mandelbrot attribs + GLuint attr_vertex2, unif_scale2, unif_offset2, unif_centre2; +} CUBE_STATE_T; + +static CUBE_STATE_T _state, *state=&_state; + +#define check() assert(glGetError() == 0) + +static void showlog(GLint shader) +{ + // Prints the compile log for a shader + char log[1024]; + glGetShaderInfoLog(shader,sizeof log,NULL,log); + printf("%d:shader:\n%s\n", shader, log); +} + +static void showprogramlog(GLint shader) +{ + // Prints the information log for a program object + char log[1024]; + glGetProgramInfoLog(shader,sizeof log,NULL,log); + printf("%d:program:\n%s\n", shader, log); +} + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void init_ogl(CUBE_STATE_T *state) +{ + int32_t success = 0; + EGLBoolean result; + EGLint num_config; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_ELEMENT_HANDLE_T dispman_element; + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + static const EGLint context_attributes[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + EGLConfig config; + + // get an EGL display connection + state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + assert(state->display!=EGL_NO_DISPLAY); + check(); + + // initialize the EGL display connection + result = eglInitialize(state->display, NULL, NULL); + assert(EGL_FALSE != result); + check(); + + // get an appropriate EGL frame buffer configuration + result = eglChooseConfig(state->display, attribute_list, &config, 1, &num_config); + assert(EGL_FALSE != result); + check(); + + // get an appropriate EGL frame buffer configuration + result = eglBindAPI(EGL_OPENGL_ES_API); + assert(EGL_FALSE != result); + check(); + + // create an EGL rendering context + state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, context_attributes); + assert(state->context!=EGL_NO_CONTEXT); + check(); + + // create an EGL window surface + success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); + assert( success >= 0 ); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = state->screen_width; + dst_rect.height = state->screen_height; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = state->screen_width << 16; + src_rect.height = state->screen_height << 16; + + dispman_display = vc_dispmanx_display_open( 0 /* LCD */); + dispman_update = vc_dispmanx_update_start( 0 ); + + dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, + 0/*layer*/, &dst_rect, 0/*src*/, + &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); + + nativewindow.element = dispman_element; + nativewindow.width = state->screen_width; + nativewindow.height = state->screen_height; + vc_dispmanx_update_submit_sync( dispman_update ); + + check(); + + state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, NULL ); + assert(state->surface != EGL_NO_SURFACE); + check(); + + // connect the context to the surface + result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); + assert(EGL_FALSE != result); + check(); + + // Set background color and clear buffers + glClearColor(0.15f, 0.25f, 0.35f, 1.0f); + glClear( GL_COLOR_BUFFER_BIT ); + + check(); +} + + +static void init_shaders(CUBE_STATE_T *state) +{ + static const GLfloat vertex_data[] = { + -1.0,-1.0,1.0,1.0, + 1.0,-1.0,1.0,1.0, + 1.0,1.0,1.0,1.0, + -1.0,1.0,1.0,1.0 + }; + const GLchar *vshader_source = + "attribute vec4 vertex;" + "varying vec2 tcoord;" + "void main(void) {" + " vec4 pos = vertex;" + " gl_Position = pos;" + " tcoord = vertex.xy*0.5+0.5;" + "}"; + + //Mandelbrot + const GLchar *mandelbrot_fshader_source = +"uniform vec4 color;" +"uniform vec2 scale;" +"uniform vec2 centre;" +"varying vec2 tcoord;" +"void main(void) {" +" float intensity;" +" vec4 color2;" +" float cr=(gl_FragCoord.x-centre.x)*scale.x;" +" float ci=(gl_FragCoord.y-centre.y)*scale.y;" +" float ar=cr;" +" float ai=ci;" +" float tr,ti;" +" float col=0.0;" +" float p=0.0;" +" int i=0;" +" for(int i2=1;i2<16;i2++)" +" {" +" tr=ar*ar-ai*ai+cr;" +" ti=2.0*ar*ai+ci;" +" p=tr*tr+ti*ti;" +" ar=tr;" +" ai=ti;" +" if (p>16.0)" +" {" +" i=i2;" +" break;" +" }" +" }" +" color2 = vec4(float(i)*0.0625,0,0,1);" +" gl_FragColor = color2;" +"}"; + + // Julia + const GLchar *julia_fshader_source = +"uniform vec4 color;" +"uniform vec2 scale;" +"uniform vec2 centre;" +"uniform vec2 offset;" +"varying vec2 tcoord;" +"uniform sampler2D tex;" +"void main(void) {" +" float intensity;" +" vec4 color2;" +" float ar=(gl_FragCoord.x-centre.x)*scale.x;" +" float ai=(gl_FragCoord.y-centre.y)*scale.y;" +" float cr=(offset.x-centre.x)*scale.x;" +" float ci=(offset.y-centre.y)*scale.y;" +" float tr,ti;" +" float col=0.0;" +" float p=0.0;" +" int i=0;" +" vec2 t2;" +" t2.x=tcoord.x+(offset.x-centre.x)*(0.5/centre.y);" +" t2.y=tcoord.y+(offset.y-centre.y)*(0.5/centre.x);" +" for(int i2=1;i2<16;i2++)" +" {" +" tr=ar*ar-ai*ai+cr;" +" ti=2.0*ar*ai+ci;" +" p=tr*tr+ti*ti;" +" ar=tr;" +" ai=ti;" +" if (p>16.0)" +" {" +" i=i2;" +" break;" +" }" +" }" +" color2 = vec4(0,float(i)*0.0625,0,1);" +" color2 = color2+texture2D(tex,t2);" +" gl_FragColor = color2;" +"}"; + + state->vshader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(state->vshader, 1, &vshader_source, 0); + glCompileShader(state->vshader); + check(); + + if (state->verbose) + showlog(state->vshader); + + state->fshader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(state->fshader, 1, &julia_fshader_source, 0); + glCompileShader(state->fshader); + check(); + + if (state->verbose) + showlog(state->fshader); + + state->mshader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(state->mshader, 1, &mandelbrot_fshader_source, 0); + glCompileShader(state->mshader); + check(); + + if (state->verbose) + showlog(state->mshader); + + // julia + state->program = glCreateProgram(); + glAttachShader(state->program, state->vshader); + glAttachShader(state->program, state->fshader); + glLinkProgram(state->program); + check(); + + if (state->verbose) + showprogramlog(state->program); + + state->attr_vertex = glGetAttribLocation(state->program, "vertex"); + state->unif_color = glGetUniformLocation(state->program, "color"); + state->unif_scale = glGetUniformLocation(state->program, "scale"); + state->unif_offset = glGetUniformLocation(state->program, "offset"); + state->unif_tex = glGetUniformLocation(state->program, "tex"); + state->unif_centre = glGetUniformLocation(state->program, "centre"); + + // mandelbrot + state->program2 = glCreateProgram(); + glAttachShader(state->program2, state->vshader); + glAttachShader(state->program2, state->mshader); + glLinkProgram(state->program2); + check(); + + if (state->verbose) + showprogramlog(state->program2); + + state->attr_vertex2 = glGetAttribLocation(state->program2, "vertex"); + state->unif_scale2 = glGetUniformLocation(state->program2, "scale"); + state->unif_offset2 = glGetUniformLocation(state->program2, "offset"); + state->unif_centre2 = glGetUniformLocation(state->program2, "centre"); + check(); + + glClearColor ( 0.0, 1.0, 1.0, 1.0 ); + + glGenBuffers(1, &state->buf); + + check(); + + // Prepare a texture image + glGenTextures(1, &state->tex); + check(); + glBindTexture(GL_TEXTURE_2D,state->tex); + check(); + // glActiveTexture(0) + glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,state->screen_width,state->screen_height,0,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,0); + check(); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + check(); + // Prepare a framebuffer for rendering + glGenFramebuffers(1,&state->tex_fb); + check(); + glBindFramebuffer(GL_FRAMEBUFFER,state->tex_fb); + check(); + glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,state->tex,0); + check(); + glBindFramebuffer(GL_FRAMEBUFFER,0); + check(); + // Prepare viewport + glViewport ( 0, 0, state->screen_width, state->screen_height ); + check(); + + // Upload vertex data to a buffer + glBindBuffer(GL_ARRAY_BUFFER, state->buf); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), + vertex_data, GL_STATIC_DRAW); + glVertexAttribPointer(state->attr_vertex, 4, GL_FLOAT, 0, 16, 0); + glEnableVertexAttribArray(state->attr_vertex); + check(); +} + + +static void draw_mandelbrot_to_texture(CUBE_STATE_T *state, GLfloat cx, GLfloat cy, GLfloat scale) +{ + // Draw the mandelbrot to a texture + glBindFramebuffer(GL_FRAMEBUFFER,state->tex_fb); + check(); + glBindBuffer(GL_ARRAY_BUFFER, state->buf); + + glUseProgram ( state->program2 ); + check(); + + glUniform2f(state->unif_scale2, scale, scale); + glUniform2f(state->unif_centre2, cx, cy); + check(); + glDrawArrays ( GL_TRIANGLE_FAN, 0, 4 ); + check(); + + glFlush(); + glFinish(); + check(); +} + +static void draw_triangles(CUBE_STATE_T *state, GLfloat cx, GLfloat cy, GLfloat scale, GLfloat x, GLfloat y) +{ + // Now render to the main frame buffer + glBindFramebuffer(GL_FRAMEBUFFER,0); + // Clear the background (not really necessary I suppose) + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + check(); + + glBindBuffer(GL_ARRAY_BUFFER, state->buf); + check(); + glUseProgram ( state->program ); + check(); + glBindTexture(GL_TEXTURE_2D,state->tex); + check(); + glUniform4f(state->unif_color, 0.5, 0.5, 0.8, 1.0); + glUniform2f(state->unif_scale, scale, scale); + glUniform2f(state->unif_offset, x, y); + glUniform2f(state->unif_centre, cx, cy); + glUniform1i(state->unif_tex, 0); // I don't really understand this part, perhaps it relates to active texture? + check(); + + glDrawArrays ( GL_TRIANGLE_FAN, 0, 4 ); + check(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glFlush(); + glFinish(); + check(); + + eglSwapBuffers(state->display, state->surface); + check(); +} + +static int get_mouse(CUBE_STATE_T *state, int *outx, int *outy) +{ + static int fd = -1; + const int width=state->screen_width, height=state->screen_height; + static int x=800, y=400; + const int XSIGN = 1<<4, YSIGN = 1<<5; + if (fd<0) { + fd = open("/dev/input/mouse0",O_RDONLY|O_NONBLOCK); + } + if (fd>=0) { + struct {char buttons, dx, dy; } m; + while (1) { + int bytes = read(fd, &m, sizeof m); + if (bytes < (int)sizeof m) goto _exit; + if (m.buttons&8) { + break; // This bit should always be set + } + read(fd, &m, 1); // Try to sync up again + } + if (m.buttons&3) + return m.buttons&3; + x+=m.dx; + y+=m.dy; + if (m.buttons&XSIGN) + x-=256; + if (m.buttons&YSIGN) + y-=256; + if (x<0) x=0; + if (y<0) y=0; + if (x>width) x=width; + if (y>height) y=height; + } +_exit: + if (outx) *outx = x; + if (outy) *outy = y; + return 0; +} + +//============================================================================== + +int main () +{ + int terminate = 0; + GLfloat cx, cy; + bcm_host_init(); + + // Clear application state + memset( state, 0, sizeof( *state ) ); + + // Start OGLES + init_ogl(state); + init_shaders(state); + cx = state->screen_width/2; + cy = state->screen_height/2; + + draw_mandelbrot_to_texture(state, cx, cy, 0.003); + while (!terminate) + { + int x, y, b; + b = get_mouse(state, &x, &y); + if (b) break; + draw_triangles(state, cx, cy, 0.003, x, y); + } + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_video/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_video/CMakeLists.txt new file mode 100755 index 0000000..fdc1182 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/CMakeLists.txt @@ -0,0 +1,10 @@ +set(EXEC hello_video.bin) +set(SRCS tsemaphore.c queue.c video.c) +#SET(pc_dependents "libtbm") + +add_executable(${EXEC} ${SRCS}) +#add_dependencies(${EXEC} ${pc_dependents}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS} -ltbm) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_video/Makefile b/host_applications/linux/apps/hello_pi/hello_video/Makefile new file mode 100755 index 0000000..c86256a --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/Makefile @@ -0,0 +1,6 @@ +OBJS=tsemaphore.o queue.o video.o +BIN=hello_video.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_video/README b/host_applications/linux/apps/hello_pi/hello_video/README new file mode 100755 index 0000000..c4922d9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/README @@ -0,0 +1 @@ +The video clip test.h264 is (c) copyright 2008, Blender Foundation / www.bigbuckbunny.org diff --git a/host_applications/linux/apps/hello_pi/hello_video/omx_comp_debug_levels.h b/host_applications/linux/apps/hello_pi/hello_video/omx_comp_debug_levels.h new file mode 100755 index 0000000..3ba1d1a --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/omx_comp_debug_levels.h @@ -0,0 +1,77 @@ +/** + @file src/omx_comp_debug_levels.h + + Define the level of debug prints on standard err. The different levels can + be composed with binary OR. + The debug levels defined here belong to OpenMAX components and IL core + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-07-16 09:39:31 +0200 (Wed, 16 Jul 2008) $ + Revision $Rev: 577 $ + Author $Author: gsent $ + +*/ + +#ifndef __OMX_COMP_DEBUG_LEVELS_H__ +#define __OMX_COMP_DEBUG_LEVELS_H__ + +#include + +/** Remove all debug output lines + */ +#define DEB_LEV_NO_OUTPUT 0 + +/** Messages explaing the reason of critical errors + */ +#define DEB_LEV_ERR 1 + +/** Messages showing values related to the test and the component/s used + */ +#define DEB_LEV_PARAMS 2 + +/** Messages representing steps in the execution. These are the simple messages, because + * they avoid iterations + */ +#define DEB_LEV_SIMPLE_SEQ 4 + +/** Messages representing steps in the execution. All the steps are described, + * also with iterations. With this level of output the performances are + * seriously compromised + */ +#define DEB_LEV_FULL_SEQ 8 + +/** Messages that indicates the beginning and the end of a function. + * It can be used to trace the execution + */ +#define DEB_LEV_FUNCTION_NAME 16 + +/** All the messages - max value + */ +#define DEB_ALL_MESS 255 + +/** \def DEBUG_LEVEL is the current level do debug output on standard err */ +#define DEBUG_LEVEL (DEB_LEV_ERR) +#if DEBUG_LEVEL > 0 +#define DEBUG(n, fmt, args...) do { if (DEBUG_LEVEL & (n)){fprintf(stderr, "OMX-" fmt, ##args);} } while (0) +#else +#define DEBUG(n, fmt, args...) +#endif + +#endif diff --git a/host_applications/linux/apps/hello_pi/hello_video/queue.c b/host_applications/linux/apps/hello_pi/hello_video/queue.c new file mode 100755 index 0000000..e6ee4b7 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/queue.c @@ -0,0 +1,134 @@ +/** + @file src/queue.c + + Implements a simple LIFO structure used for queueing OMX buffers. + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-06-27 12:00:23 +0200 (Fri, 27 Jun 2008) $ + Revision $Rev: 554 $ + Author $Author: pankaj_sen $ +*/ + +#include +#include +#include + +#include "queue.h" +#include "omx_comp_debug_levels.h" + +/** Initialize a queue descriptor + * + * @param queue The queue descriptor to initialize. + * The user needs to allocate the queue + */ +void queue_init(queue_t* queue) { + int i; + qelem_t* newelem; + qelem_t* current; + queue->first = malloc(sizeof(qelem_t)); + memset(queue->first, 0, sizeof(qelem_t)); + current = queue->last = queue->first; + queue->nelem = 0; + for (i = 0; iq_forw = newelem; + current = newelem; + } + current->q_forw = queue->first; + + pthread_mutex_init(&queue->mutex, NULL); +} + +/** Deinitialize a queue descriptor + * flushing all of its internal data + * + * @param queue the queue descriptor to dump + */ +void queue_deinit(queue_t* queue) { + int i; + qelem_t* current; + current = queue->first; + for (i = 0; iq_forw; + free(queue->first); + queue->first = current; + } + } + if(queue->first) { + free(queue->first); + queue->first = NULL; + } + pthread_mutex_destroy(&queue->mutex); +} + +/** Enqueue an element to the given queue descriptor + * + * @param queue the queue descritpor where to queue data + * + * @param data the data to be enqueued + */ +void queue(queue_t* queue, void* data) { + if (queue->last->data != NULL) { + return; + } + pthread_mutex_lock(&queue->mutex); + queue->last->data = data; + queue->last = queue->last->q_forw; + queue->nelem++; + pthread_mutex_unlock(&queue->mutex); +} + +/** Dequeue an element from the given queue descriptor + * + * @param queue the queue descriptor from which to dequeue the element + * + * @return the element that has bee dequeued. If the queue is empty + * a NULL value is returned + */ +void* dequeue(queue_t* queue) { + void* data; + if (queue->first->data == NULL) { + return NULL; + } + pthread_mutex_lock(&queue->mutex); + data = queue->first->data; + queue->first->data = NULL; + queue->first = queue->first->q_forw; + queue->nelem--; + pthread_mutex_unlock(&queue->mutex); + + return data; +} + +/** Returns the number of elements hold in the queue + * + * @param queue the requested queue + * + * @return the number of elements in the queue + */ +int getquenelem(queue_t* queue) { + int qelem; + pthread_mutex_lock(&queue->mutex); + qelem = queue->nelem; + pthread_mutex_unlock(&queue->mutex); + return qelem; +} diff --git a/host_applications/linux/apps/hello_pi/hello_video/queue.h b/host_applications/linux/apps/hello_pi/hello_video/queue.h new file mode 100755 index 0000000..745ca70 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/queue.h @@ -0,0 +1,92 @@ +/** + @file src/queue.h + + Implements a simple LIFO structure used for queueing OMX buffers. + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-06-27 12:00:23 +0200 (Fri, 27 Jun 2008) $ + Revision $Rev: 554 $ + Author $Author: pankaj_sen $ +*/ + +#ifndef __TQUEUE_H__ +#define __TQUEUE_H__ + +#include +/** Maximum number of elements in a queue + */ +#define MAX_QUEUE_ELEMENTS 10 +/** Output port queue element. Contains an OMX buffer header type + */ +typedef struct qelem_t qelem_t; +struct qelem_t{ + qelem_t* q_forw; + void* data; +}; + +/** This structure contains the queue + */ +typedef struct queue_t{ + qelem_t* first; /**< Output buffer queue head */ + qelem_t* last; /**< Output buffer queue tail */ + int nelem; /**< Number of elements in the queue */ + pthread_mutex_t mutex; +} queue_t; + +/** Initialize a queue descriptor + * + * @param queue The queue descriptor to initialize. + * The user needs to allocate the queue + */ +void queue_init(queue_t* queue); + +/** Deinitialize a queue descriptor + * flushing all of its internal data + * + * @param queue the queue descriptor to dump + */ +void queue_deinit(queue_t* queue); + +/** Enqueue an element to the given queue descriptor + * + * @param queue the queue descritpor where to queue data + * + * @param data the data to be enqueued + */ +void queue(queue_t* queue, void* data); + +/** Dequeue an element from the given queue descriptor + * + * @param queue the queue descriptor from which to dequeue the element + * + * @return the element that has bee dequeued. If the queue is empty + * a NULL value is returned + */ +void* dequeue(queue_t* queue); + +/** Returns the number of elements hold in the queue + * + * @param queue the requested queue + * + * @return the number of elements in the queue + */ +int getquenelem(queue_t* queue); + +#endif diff --git a/host_applications/linux/apps/hello_pi/hello_video/tags b/host_applications/linux/apps/hello_pi/hello_video/tags new file mode 100755 index 0000000..eb7bd70 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/tags @@ -0,0 +1,10 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.9~svn20110310 // +BIN Makefile /^BIN=hello_video.bin$/;" m +OBJS Makefile /^OBJS=video.o$/;" m +main video.c /^int main (int argc, char **argv)$/;" f signature:(int argc, char **argv) +video_decode_test video.c /^static int video_decode_test(char *filename)$/;" f file: signature:(char *filename) diff --git a/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.c b/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.c new file mode 100755 index 0000000..6f2b6ad --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.c @@ -0,0 +1,111 @@ +/** + @file src/tsemaphore.c + + Implements a simple inter-thread semaphore so not to have to deal with IPC + creation and the like. + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-06-27 12:00:23 +0200 (Fri, 27 Jun 2008) $ + Revision $Rev: 554 $ + Author $Author: pankaj_sen $ +*/ + +#include +#include +#include +#include "tsemaphore.h" +#include "omx_comp_debug_levels.h" + +/** Initializes the semaphore at a given value + * + * @param tsem the semaphore to initialize + * @param val the initial value of the semaphore + * + */ +void tsem_init(tsem_t* tsem, unsigned int val) { + pthread_cond_init(&tsem->condition, NULL); + pthread_mutex_init(&tsem->mutex, NULL); + tsem->semval = val; +} + +/** Destroy the semaphore + * + * @param tsem the semaphore to destroy + */ +void tsem_deinit(tsem_t* tsem) { + pthread_cond_destroy(&tsem->condition); + pthread_mutex_destroy(&tsem->mutex); +} + +/** Decreases the value of the semaphore. Blocks if the semaphore + * value is zero. + * + * @param tsem the semaphore to decrease + */ +void tsem_down(tsem_t* tsem) { + pthread_mutex_lock(&tsem->mutex); + while (tsem->semval == 0) { + pthread_cond_wait(&tsem->condition, &tsem->mutex); + } + tsem->semval--; + pthread_mutex_unlock(&tsem->mutex); +} + +/** Increases the value of the semaphore + * + * @param tsem the semaphore to increase + */ +void tsem_up(tsem_t* tsem) { + pthread_mutex_lock(&tsem->mutex); + tsem->semval++; + pthread_cond_signal(&tsem->condition); + pthread_mutex_unlock(&tsem->mutex); +} + +/** Reset the value of the semaphore + * + * @param tsem the semaphore to reset + */ +void tsem_reset(tsem_t* tsem) { + pthread_mutex_lock(&tsem->mutex); + tsem->semval=0; + pthread_mutex_unlock(&tsem->mutex); +} + +/** Wait on the condition. + * + * @param tsem the semaphore to wait + */ +void tsem_wait(tsem_t* tsem) { + pthread_mutex_lock(&tsem->mutex); + pthread_cond_wait(&tsem->condition, &tsem->mutex); + pthread_mutex_unlock(&tsem->mutex); +} + +/** Signal the condition,if waiting + * + * @param tsem the semaphore to signal + */ +void tsem_signal(tsem_t* tsem) { + pthread_mutex_lock(&tsem->mutex); + pthread_cond_signal(&tsem->condition); + pthread_mutex_unlock(&tsem->mutex); +} + diff --git a/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.h b/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.h new file mode 100755 index 0000000..4bc1ab2 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/tsemaphore.h @@ -0,0 +1,87 @@ +/** + @file src/tsemaphore.h + + Implements a simple inter-thread semaphore so not to have to deal with IPC + creation and the like. + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-06-27 12:00:23 +0200 (Fri, 27 Jun 2008) $ + Revision $Rev: 554 $ + Author $Author: pankaj_sen $ + +*/ + +#ifndef __TSEMAPHORE_H__ +#define __TSEMAPHORE_H__ + +/** The structure contains the semaphore value, mutex and green light flag + */ +typedef struct tsem_t{ + pthread_cond_t condition; + pthread_mutex_t mutex; + unsigned int semval; +}tsem_t; + +/** Initializes the semaphore at a given value + * + * @param tsem the semaphore to initialize + * + * @param val the initial value of the semaphore + */ +void tsem_init(tsem_t* tsem, unsigned int val); + +/** Destroy the semaphore + * + * @param tsem the semaphore to destroy + */ +void tsem_deinit(tsem_t* tsem); + +/** Decreases the value of the semaphore. Blocks if the semaphore + * value is zero. + * + * @param tsem the semaphore to decrease + */ +void tsem_down(tsem_t* tsem); + +/** Increases the value of the semaphore + * + * @param tsem the semaphore to increase + */ +void tsem_up(tsem_t* tsem); + +/** Reset the value of the semaphore + * + * @param tsem the semaphore to reset + */ +void tsem_reset(tsem_t* tsem); + +/** Wait on the condition. + * + * @param tsem the semaphore to wait + */ +void tsem_wait(tsem_t* tsem); + +/** Signal the condition,if waiting + * + * @param tsem the semaphore to signal + */ +void tsem_signal(tsem_t* tsem); + +#endif diff --git a/host_applications/linux/apps/hello_pi/hello_video/user_debug_levels.h b/host_applications/linux/apps/hello_pi/hello_video/user_debug_levels.h new file mode 100755 index 0000000..b28b07f --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/user_debug_levels.h @@ -0,0 +1,73 @@ +/** + @file test/components/common/user_debug_levels.h + + Define the level of debug prints on standard err. The different levels can + be composed with binary OR. + The debug levels defined here belong to the test applications + + Copyright (C) 2007-2008 STMicroelectronics + Copyright (C) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA + + $Date: 2008-07-16 09:39:31 +0200 (Wed, 16 Jul 2008) $ + Revision $Rev: 577 $ + Author $Author: gsent $ + +*/ + + +/** Remove all debug output lines + */ +#define DEB_LEV_NO_OUTPUT 0 +/** Messages explaing the reason of critical errors + */ +#define DEB_LEV_ERR 1 +/** Messages showing values related to the test and the component/s used + */ +#define DEB_LEV_PARAMS 2 +/** Messages representing steps in the execution. These are the simple messages, because + * they avoid iterations + */ +#define DEB_LEV_SIMPLE_SEQ 4 +/** Messages representing steps in the execution. All the steps are described, + * also with iterations. With this level of output the performance is + * seriously compromised + */ +#define DEB_LEV_FULL_SEQ 8 +/** Messages that indicate the beginning and the end of a function. + * It can be used to trace the execution + */ +#define DEB_LEV_FUNCTION_NAME 16 + +/** Messages that are the default test application output. These message should be + * shown every time + */ +#define DEFAULT_MESSAGES 32 + +/** All the messages - max value + */ +#define DEB_ALL_MESS 255 + + +/** \def DEBUG_LEVEL is the current level do debug output on standard err */ +#define DEBUG_LEVEL (DEB_LEV_ERR | DEFAULT_MESSAGES) +#if DEBUG_LEVEL > 0 +#define DEBUG(n, args...) do { if (DEBUG_LEVEL & (n)){fprintf(stderr, args);} } while (0) +#else +#define DEBUG(n, args...) +#endif + diff --git a/host_applications/linux/apps/hello_pi/hello_video/video.c b/host_applications/linux/apps/hello_pi/hello_video/video.c new file mode 100755 index 0000000..3275d57 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/video.c @@ -0,0 +1,1055 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Video deocode demo using OpenMAX IL though the ilcient helper library + +#include +#include +#include +#include "video.h" +/* +#define VERSIONMAJOR 1 +#define VERSIONMINOR 1 +#define VERSIONREVISION 0 +#define VERSIONSTEP 0 + +#define OMX_VERSION ((VERSIONSTEP<<24) | (VERSIONREVISION<<16) | (VERSIONMINOR<<8) | VERSIONMAJOR) + +#define OMX_INIT_STRUCTURE(a) \ +memset(&(a), 0, sizeof(a)); \ +(a).nSize = sizeof(a); \ +(a).nVersion.nVersion = OMX_VERSION; \ +(a).nVersion.s.nVersionMajor = VERSIONMAJOR; \ +(a).nVersion.s.nVersionMinor = VERSIONMINOR; \ +(a).nVersion.s.nRevision = VERSIONREVISION; \ +(a).nVersion.s.nStep = VERSIONSTEP + +static void setHeader(OMX_PTR header, OMX_U32 size) { + OMX_VERSIONTYPE* ver = (OMX_VERSIONTYPE*)(header + sizeof(OMX_U32)); + *((OMX_U32*)header) = size; + + ver->s.nVersionMajor = VERSIONMAJOR; + ver->s.nVersionMinor = VERSIONMINOR; + ver->s.nRevision = VERSIONREVISION; + ver->s.nStep = VERSIONSTEP; +} +*/ + +/* +void allocateBuffer(int index, int actualBuffer, int width, int height) +{ + int i, err; + tbm_bo_handle handle_bo; + int y_size, uv_size; + + y_size = ALIGN(width, 16) * ALIGN(height, 16); + uv_size = y_size /2; + + for (i = 0; i < actualBuffer; i++) { + + unsigned char *tmp; + + //tmp = (MMVideoBuffer*) malloc(sizeof(MMVideoBuffer)); + //tmp->handle.bo[0] = tbm_bo_alloc(hBufmgr, y_size, TBM_BO_WC); + handle_bo = tbm_bo_get_handle(tmp->handle.bo[0], TBM_DEVICE_CPU); + tmp = handle_bo.ptr; + //handle_bo = tbm_bo_get_handle(tmp->handle.bo[0], TBM_DEVICE_MM); + //tmp->handle.dmabuf_fd[0] = handle_bo.u32; + //tmp->size[0] = y_size; + + + if (index == 1) { + tmp->handle.bo[1] = tbm_bo_alloc(hBufmgr, uv_size, TBM_BO_WC); + handle_bo = tbm_bo_get_handle(tmp->handle.bo[1], TBM_DEVICE_CPU); + tmp->data[1] = handle_bo.ptr; + handle_bo = tbm_bo_get_handle(tmp->handle.bo[1], TBM_DEVICE_MM); + tmp->handle.dmabuf_fd[1] = handle_bo.u32; + tmp->size[1] = uv_size; + } + + if (index == 0) { + err = OMX_UseBuffer(appPriv->videodechandle, &pInBuffer[i], 0, NULL, y_size, tmp); + printf( "index : %d -> OMX_UseBuffer :%x\n", index, err); + queue(pInBufQueue, pInBuffer[i]); + //queue(pInBufQueue, tmp); + } else if (index == 1) { + err = OMX_UseBuffer(appPriv->videodechandle, &pOutBuffer[i], 1, NULL, y_size + uv_size, tmp); + printf( "index : %d -> OMX_UseBuffer :%i\n", index, err); + queue(pOutBufQueue, pOutBuffer[i]); + } + + } + +} +*/ + +#define COMPONENT_NAME_BASE "OMX.broadcom.video_decode" +#define BASE_ROLE "video_decoder.avc" + +OMX_VIDEO_PARAM_PORTFORMATTYPE format; +OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; +COMPONENT_T *video_decode = NULL; +COMPONENT_T *list[5]; +TUNNEL_T tunnel[4]; +ILCLIENT_T *client; +FILE *in; +int status = 0; +unsigned int data_len = 0; +int err; +int actualInBufferCount; +int actualOutBufferCount; +int nInBufferSize, nOutBufferSize; +int nInBufferAlignment, nOutBufferAlignment; +OMX_BUFFERHEADERTYPE *pInBuffer[20], *pOutBuffer[16]; +OMX_STRING full_component_name; +appPrivateType* appPriv; +tbm_bufmgr hBufmgr; +int drm_fd; +queue_t* pInBufQueue; +queue_t* pOutBufQueue; + +OMX_CALLBACKTYPE videodeccallbacks = { + .EventHandler = videodecEventHandler, + .EmptyBufferDone = videodecEmptyBufferDone, + .FillBufferDone = videodecFillBufferDone + }; +/* +static void setHeader(OMX_PTR header, OMX_U32 size) { + OMX_VERSIONTYPE* ver = (OMX_VERSIONTYPE*)(header + sizeof(OMX_U32)); + *((OMX_U32*)header) = size; + + ver->s.nVersionMajor = VERSIONMAJOR; + ver->s.nVersionMinor = VERSIONMINOR; + ver->s.nRevision = VERSIONREVISION; + ver->s.nStep = VERSIONSTEP; +} +*/ +OMX_ERRORTYPE test_OMX_ComponentNameEnum() { + char * name; + int index; + + OMX_ERRORTYPE err = OMX_ErrorNone; + + printf("GENERAL TEST %s\n", __func__); + + name = malloc(OMX_MAX_STRINGNAME_SIZE); + index = 0; + while(1) { + err = OMX_ComponentNameEnum (name, OMX_MAX_STRINGNAME_SIZE, index); + if ((name != NULL) && (err == OMX_ErrorNone)) { + printf("component %i is %s\n", index, name); + } else break; + if (err != OMX_ErrorNone) break; + index++; + } + free(name); + name = NULL; + printf("GENERAL TEST %s result %i\n", __func__, err); + return err; +} + +OMX_ERRORTYPE test_OMX_RoleEnum(OMX_STRING component_name) { + OMX_U32 no_of_roles; + OMX_U8 **string_of_roles; + OMX_ERRORTYPE err = OMX_ErrorNone; + int index; + + printf("GENERAL TEST %s\n", __func__); + printf("Getting roles of %s. Passing Null first...\n", component_name); + err = OMX_GetRolesOfComponent(component_name, &no_of_roles, NULL); + if (err != OMX_ErrorNone) { + printf("Not able to retrieve the number of roles of the given component\n"); + printf("GENERAL TEST %s result %i\n", __func__, err); + return err; + } + printf("The number of roles for the component %s is: %i\n", component_name, (int)no_of_roles); + + if(no_of_roles == 0) { + printf("The Number or roles is 0.\nThe component selected is not correct for the purpose of this test.\nExiting...\n"); + err = OMX_ErrorInvalidComponentName; + } else { + string_of_roles = malloc(no_of_roles * sizeof(OMX_STRING)); + for (index = 0; index < no_of_roles; index++) { + *(string_of_roles + index) = malloc(no_of_roles * OMX_MAX_STRINGNAME_SIZE); + } + printf("...then buffers\n"); + + err = OMX_GetRolesOfComponent(component_name, &no_of_roles, string_of_roles); + if (err != OMX_ErrorNone) { + printf("Not able to retrieve the roles of the given component\n"); + } else if(string_of_roles != NULL) { + for (index = 0; index < no_of_roles; index++) { + printf("The role %i for the component: %s \n", (index + 1), *(string_of_roles+index)); + } + } else { + printf("role string is NULL!!! Exiting...\n"); + err = OMX_ErrorInvalidComponentName; + } + } + printf("GENERAL TEST %s result %i\n", __func__, err); + return err; +} + +OMX_ERRORTYPE test_OMX_ComponentEnumByRole(OMX_STRING role_name) { + OMX_U32 no_of_comp_per_role; + OMX_U8 **string_of_comp_per_role; + OMX_ERRORTYPE err; + int index; + + printf("GENERAL TEST %s\n", __func__); + string_of_comp_per_role = malloc (10 * sizeof(OMX_STRING)); + for (index = 0; index < 10; index++) { + string_of_comp_per_role[index] = malloc(OMX_MAX_STRINGNAME_SIZE); + } + + printf("Getting number of components per role for %s\n", role_name); + + err = OMX_GetComponentsOfRole(role_name, &no_of_comp_per_role, NULL); + if (err != OMX_ErrorNone) { + printf("Not able to retrieve the number of components of a given role\n"); + printf( "GENERAL TEST %s result %i\n", __func__, err); + return err; + } + printf("Number of components per role for %s is %i\n", role_name, (int)no_of_comp_per_role); + + err = OMX_GetComponentsOfRole(role_name, &no_of_comp_per_role, string_of_comp_per_role); + if (err != OMX_ErrorNone) { + printf("Not able to retrieve the components of a given role\n"); + printf( "GENERAL TEST %s result %i\n",__func__, err); + return err; + } + + printf(" The components are:\n"); + for (index = 0; index < no_of_comp_per_role; index++) { + printf("%s\n", string_of_comp_per_role[index]); + } + for (index = 0; index<10; index++) { + if(string_of_comp_per_role[index]) { + free(string_of_comp_per_role[index]); + string_of_comp_per_role[index] = NULL; + } + } + + if(string_of_comp_per_role) { + free(string_of_comp_per_role); + string_of_comp_per_role = NULL; + } + printf("GENERAL TEST %s result OMX_ErrorNone\n", __func__); + return OMX_ErrorNone; +} + +OMX_ERRORTYPE test_OpenClose(OMX_STRING component_name) { + OMX_ERRORTYPE err = OMX_ErrorNone; + + printf("GENERAL TEST %s\n",__func__); + err = OMX_GetHandle(&appPriv->videodechandle, component_name, NULL /*appPriv */, &videodeccallbacks); + if(err != OMX_ErrorNone) { + printf("No component found\n"); + } else { + err = OMX_FreeHandle(appPriv->videodechandle); + } + printf("GENERAL TEST %s result %i\n", __func__, err); + return err; +} + +void allocateBuffer(int index, int actualBuffer, int width, int height, int aligned_size) +{ + int i, err; + tbm_bo_handle handle_bo; + int y_size, uv_size; + int size; + + y_size = ALIGN(width + 128, 16) * ALIGN(height + 128, 16); + uv_size = y_size /2; + size = y_size + uv_size; + + for (i = 0; i < actualBuffer; i++) { + + void *tmp; + unsigned char *buf; + + + + if (index == 130) { + buf = (unsigned char*) malloc (sizeof(unsigned char) * aligned_size); + err = OMX_UseBuffer(appPriv->videodechandle, &pInBuffer[i], 130, NULL, ALIGN(aligned_size, 16), buf); + printf("index : %d -> OMX_UseBuffer : %p, %x\n", index, pInBuffer[i], err); + //queue(pInBufQueue, pInBuffer[i]); + } else if (index == 131) { + tmp = tbm_bo_alloc(hBufmgr, ALIGN(aligned_size, 16), TBM_BO_WC); + handle_bo = tbm_bo_get_handle(tmp, TBM_DEVICE_CPU); + buf = handle_bo.ptr; + + err = OMX_UseBuffer(appPriv->videodechandle, &pOutBuffer[i], 131, NULL, ALIGN(aligned_size, 16), buf); + printf("index : %d -> OMX_UseBuffer : %p, %x\n", index, pOutBuffer[i], err); + //queue(pOutBufQueue, pOutBuffer[i]); + } else + printf("invalid port\n"); +/* + + if (index == 1) { + tmp->handle.bo[1] = tbm_bo_alloc(hBufmgr, uv_size, TBM_BO_WC); + handle_bo = tbm_bo_get_handle(tmp->handle.bo[1], TBM_DEVICE_CPU); + tmp->data[1] = handle_bo.ptr; + handle_bo = tbm_bo_get_handle(tmp->handle.bo[1], TBM_DEVICE_MM); + tmp->handle.dmabuf_fd[1] = handle_bo.u32; + tmp->size[1] = uv_size; + } + + if (index == 0) { + err = OMX_UseBuffer(appPriv->videodechandle, &pInBuffer[i], 0, NULL, y_size, tmp); + DEBUG(DEFAULT_MESSAGES, "index : %d -> OMX_UseBuffer :%x\n", index, err); + queue(pInBufQueue, pInBuffer[i]); + //queue(pInBufQueue, tmp); + } else if (index == 1) { + err = OMX_UseBuffer(appPriv->videodechandle, &pOutBuffer[i], 1, NULL, y_size + uv_size, tmp); + DEBUG(DEFAULT_MESSAGES, "index : %d -> OMX_UseBuffer :%i\n", index, err); + queue(pOutBufQueue, pOutBuffer[i]); + } +*/ + } + +} +/* +void deallocateBuffer(int index, int actualBuffer) +{ + int i, err; + MMVideoBuffer *pTmp; + + for (i = 0; i < actualBuffer; i++) { + if (index == 0) { + //pTmp = (MMVideoBuffer*)dequeue(pInBufQueue); + pTmp = pInBuffer[i]; + } else if (index == 1) { + //pTmp = (MMVideoBuffer*)dequeue(pOutBufQueue); + pTmp = pOutBuffer[i]; + } + + tbm_bo_unref(pTmp->handle.bo[0]); + if (index == 1) + tbm_bo_unref(pTmp->handle.bo[1]); + + err = OMX_FreeBuffer (appPriv->videodechandle, index, pTmp); + DEBUG(DEFAULT_MESSAGES, "%d port %d buffer free : %d\n", index, i, err); + } +} +*/ +const char * omx_state_to_string(OMX_STATETYPE state) +{ + switch (state) + { + case OMX_StateInvalid: return "Invalid"; break; + case OMX_StateLoaded: return "Loaded"; break; + case OMX_StateIdle: return "Idle"; break; + case OMX_StateExecuting: return "Executing"; break; + case OMX_StatePause: return "Pause"; break; + case OMX_StateWaitForResources: return "Wait for resources"; break; + default: return "Bad state"; break; + } +} + +void wait_for(appPrivateType *app, OMX_STATETYPE state) +{ + while (app->state != state) + { + printf("wait for %s \n", omx_state_to_string(state)); + tsem_down(app->stateSem); + if (app->state != state) { + printf("we got an event, but still waiting for %s \n", omx_state_to_string(state)); + } + + } +} + +unsigned int FromByteStream2NalUnit(FILE *ByteStream, unsigned char *Nal) +{ + unsigned int NalLeng=0; + + unsigned char Val, ZeroCount, i; + ZeroCount = 0; + if(feof(ByteStream)) + return 0; + Val = fgetc(ByteStream); + while(!Val) + { + if(ZeroCount == 3) + break; + ZeroCount++; + Val = fgetc(ByteStream); + } + + if( (ZeroCount != 3) || (Val != 1) ) + { + for(i=0;i 0x7e)) + buff[i % 16] = '.'; + else + buff[i % 16] = pc[i]; + buff[(i % 16) + 1] = '\0'; + } + + while ((i % 16) != 0) { + printf(" "); + i++; + } + printf(" %s\n", buff); +} + +static int video_decode_test(char *filename) +{ + int i; + int offset; + int data_read; + int fd; + + memset(list, 0, sizeof(list)); + + if((in = fopen(filename, "rb")) == NULL) + return -2; + if ((fd = open(filename, O_RDONLY)) == -1) + return -2; +/* + if((client = ilclient_init()) == NULL) + { + fclose(in); + return -3; + } +*/ + /* Initialize application private data */ + appPriv = malloc(sizeof(appPrivateType)); + appPriv->decoderEventSem = malloc(sizeof(tsem_t)); + appPriv->stateSem = malloc(sizeof(tsem_t)); + appPriv->state = OMX_StateInvalid; + + appPriv->eofSem = malloc(sizeof(tsem_t)); + tsem_init(appPriv->decoderEventSem, 0); + tsem_init(appPriv->stateSem, 0); + tsem_init(appPriv->eofSem, 0); + + printf("Init the OMX Core\n"); + + if(OMX_Init() != OMX_ErrorNone) + { + ilclient_destroy(client); + fclose(in); + return -4; + } + printf("Omx core is initialized \n"); + +/* + // create video_decode + if (ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS | ILCLIENT_ENABLE_OUTPUT_BUFFERS) != 0) + status = -14; + list[0] = video_decode; +*/ + + printf("------------------------------------\n"); + test_OMX_ComponentNameEnum(); + printf("------------------------------------\n"); + test_OMX_RoleEnum(COMPONENT_NAME_BASE); + printf("------------------------------------\n"); + test_OMX_ComponentEnumByRole(BASE_ROLE); + printf("------------------------------------\n"); + test_OpenClose(COMPONENT_NAME_BASE); + printf("------------------------------------\n"); + + full_component_name = malloc(sizeof(char*) * OMX_MAX_STRINGNAME_SIZE); + strcpy(full_component_name, COMPONENT_NAME_BASE); + printf("The component selected for decoding is %s\n", full_component_name); + + err = OMX_GetHandle(&appPriv->videodechandle, full_component_name, NULL, &videodeccallbacks); + if(err != OMX_ErrorNone){ + printf("No video decoder component found. Exiting...\n"); + exit(1); + } else { + printf("Found The component for decoding is %s\n", full_component_name); + } + + /** sending command to video decoder component to go to idle state */ + //pInBuffer1 = pInBuffer2 = NULL; + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandStateSet, OMX_StateIdle, NULL); + + //wait_for(appPriv, OMX_StateIdle); + printf("state changed to Idle :%i\n", err); + + hBufmgr = tbm_bufmgr_init(drm_fd); + if(hBufmgr == NULL){ + printf("TBM initialization failed\n"); + } + + + OMX_PARAM_PORTDEFINITIONTYPE port_def; + memset(&port_def, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + port_def.nVersion.nVersion = OMX_VERSION; + port_def.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + port_def.nPortIndex = 130; + + err = OMX_GetParameter (appPriv->videodechandle, OMX_IndexParamPortDefinition, &port_def); + printf( "OMX_GetParameter :%x, input buffer count : %d, nBufferSize : %d, nBufferAlignment : %d\n", err, port_def.nBufferCountActual, port_def.nBufferSize, port_def.nBufferAlignment); + actualInBufferCount = port_def.nBufferCountActual; + nInBufferSize = port_def.nBufferSize; + nInBufferAlignment = port_def.nBufferAlignment; + + port_def.nPortIndex = 131; + err = OMX_GetParameter (appPriv->videodechandle, OMX_IndexParamPortDefinition, &port_def); + printf( "OMX_GetParameter :%x, output buffer count : %d, nBufferSize : %d, nBufferAlignment : %d\n", err, port_def.nBufferCountActual, port_def.nBufferSize, port_def.nBufferAlignment); + actualOutBufferCount = port_def.nBufferCountActual; + nOutBufferSize = port_def.nBufferSize; + nOutBufferAlignment = port_def.nBufferAlignment; + + + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandPortDisable, 130, NULL); + printf( "Input port OMX_CommandPortDisable : %x\n", err); + + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandPortDisable, 131, NULL); + printf( "Output port OMX_CommandPortDisable : %x\n", err); + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 130; + format.eCompressionFormat = OMX_VIDEO_CodingAVC; + + err = OMX_SetParameter(appPriv->videodechandle, OMX_IndexParamVideoPortFormat, &format); + printf("Input OMX_IndexParamVideoPortFormat: %x\n", err); + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 131; + err = OMX_SetParameter(appPriv->videodechandle, OMX_IndexParamVideoPortFormat, &format); + printf("Output OMX_IndexParamVideoPortFormat: %x\n", err); + + + pInBufQueue = calloc(1,sizeof(queue_t)); + pOutBufQueue = calloc(1,sizeof(queue_t)); + + queue_init(pInBufQueue); + queue_init(pOutBufQueue); + + //err = ilclient_change_component_state(video_decode, OMX_StateExecuting); + //printf("ilclient_change_component_state -> OMX_StateExecuting : err : %d\n", err); + + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandPortEnable, 130, NULL); + printf( "Input port OMX_CommandPortEnable : %x\n", err); + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandPortEnable, 131, NULL); + printf( "Output port OMX_CommandPortEnable : %x\n", err); + + allocateBuffer(130, actualInBufferCount, 1280, 720, nInBufferSize); + allocateBuffer(131, actualOutBufferCount, 1280, 720, nOutBufferSize); + + wait_for(appPriv, OMX_StateIdle); + + /** sending command to video decoder component to go to executing state */ + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandStateSet, OMX_StateExecuting, NULL); + printf("send Excuting :%d\n", err); + + wait_for(appPriv, OMX_StateExecuting); + printf("Excuting state\n"); + + for (i = 0; i < actualOutBufferCount; i++) { + err = OMX_FillThisBuffer(appPriv->videodechandle, pOutBuffer[i]); + printf( "FillThisBuffer : %p, %x\n",pOutBuffer[i], err); + } + + for (i = 0; i < actualInBufferCount; i++) { + //pthread_mutex_lock(&mutex); + unsigned char *pTmp; + offset = 0; + + pTmp = pInBuffer[i]->pBuffer; + pTmp[offset++] = 0; + pTmp[offset++] = 0; + pTmp[offset++] = 0; + pTmp[offset++] = 1; + + data_read = FromByteStream2NalUnit(in, pTmp + offset); + + pInBuffer[i]->nFilledLen = data_read + offset; + pInBuffer[i]->nOffset = 0; + hex_dump("src0", pInBuffer[i]->pBuffer, 16); + + //pthread_mutex_unlock(&mutex); + + err = OMX_EmptyThisBuffer(appPriv->videodechandle, pInBuffer[i]); + printf("EmptyThisBuffer %x, %d\n", pInBuffer[i]->pBuffer, data_read + offset); + usleep(100000); + } + + tsem_down(appPriv->decoderEventSem); + printf("port setting changed\n"); + + err = OMX_SendCommand(appPriv->videodechandle, OMX_CommandPortDisable, 131, NULL); + printf("Sending Port Disable Command : %d\n", err); +#if 0 + if(status == 0 && + OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone && + ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0 && + ilclient_enable_port_buffers(video_decode, 131, NULL, NULL, NULL) == 0) + { + OMX_BUFFERHEADERTYPE *buf, *outbuf; + int port_settings_changed = 0; + int first_packet = 1; + + + while ((outbuf = ilclient_get_output_buffer(video_decode, 131, 1)) != NULL) + { + unsigned char *tmp = outbuf->pBuffer; + + err = OMX_FillThisBuffer(ILC_GET_HANDLE(video_decode), outbuf); + if (err != OMX_ErrorNone) + { + printf("Failed to call OMX_FillThisBuffer\n"); + status = -6; + break; + } + printf("call OMX_FillThisBuffer : %d\n", err); + } + + while((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL) + { + // feed data and wait until we get port settings changed + unsigned char *dest = buf->pBuffer; + + data_len += fread(dest, 1, buf->nAllocLen-data_len, in); + printf( "data_len :%i\n", data_len); + + if(port_settings_changed == 0 && + ((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) || + (data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, + ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) + { + port_settings_changed = 1; + + //err = ilclient_change_component_state(video_decode, OMX_StateExecuting); + //printf("ilclient_change_component_state -> OMX_StateExecuting : err : %d\n", err); + printf("port setting changed \n"); + port_def.nPortIndex = 131; + err = OMX_GetParameter (ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &port_def); + printf( "OMX_GetParameter :%i\n", err); + actualOutBufferCount = port_def.nBufferCountActual; + printf("outbuf count :%d\n", actualOutBufferCount); + } + + if(!data_len) + break; + + buf->nFilledLen = data_len; + data_len = 0; + + buf->nOffset = 0; + if(first_packet) + { + buf->nFlags = OMX_BUFFERFLAG_STARTTIME; + first_packet = 0; + } + else + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + { + printf("Failed to call OMX_EmptyThisBuffer\n"); + status = -6; + break; + } + } +/* + while((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL) + { + // feed data and wait until we get port settings changed + unsigned char *dest = buf->pBuffer; + + data_len += fread(dest, 1, buf->nAllocLen-data_len, in); + + if(port_settings_changed == 0 && + ((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) || + (data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, + ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) + { + port_settings_changed = 1; + + if(ilclient_setup_tunnel(tunnel, 0, 0) != 0) + { + status = -7; + break; + } + + ilclient_change_component_state(video_scheduler, OMX_StateExecuting); + + // now setup tunnel to video_render + if(ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0) + { + status = -12; + break; + } + + ilclient_change_component_state(video_render, OMX_StateExecuting); + } + if(!data_len) + break; + + buf->nFilledLen = data_len; + data_len = 0; + + buf->nOffset = 0; + if(first_packet) + { + buf->nFlags = OMX_BUFFERFLAG_STARTTIME; + first_packet = 0; + } + else + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + { + status = -6; + break; + } + } + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + status = -20; + + // wait for EOS from render + ilclient_wait_for_event(video_render, OMX_EventBufferFlag, 90, 0, OMX_BUFFERFLAG_EOS, 0, + ILCLIENT_BUFFER_FLAG_EOS, -1); + + // need to flush the renderer to allow video_decode to disable its input port + ilclient_flush_tunnels(tunnel, 0); +*/ + } +#endif + fclose(in); + + //ilclient_disable_tunnel(tunnel); + //ilclient_disable_tunnel(tunnel+1); + //ilclient_disable_tunnel(tunnel+2); + //ilclient_disable_port_buffers(video_decode, 130, NULL, NULL, NULL); + //ilclient_teardown_tunnels(tunnel); + + //ilclient_state_transition(list, OMX_StateIdle); + //ilclient_state_transition(list, OMX_StateLoaded); + + //ilclient_cleanup_components(list); + + OMX_Deinit(); + + //ilclient_destroy(client); + return status; +} + +int main (int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + exit(1); + } + bcm_host_init(); + return video_decode_test(argv[1]); +} + + +OMX_ERRORTYPE videodecEventHandler( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_EVENTTYPE eEvent, + OMX_OUT OMX_U32 Data1, + OMX_OUT OMX_U32 Data2, + OMX_OUT OMX_PTR pEventData) { + + OMX_ERRORTYPE err = OMX_ErrorNone; + + printf("Hi there, I am in the %s callback\n", __func__); + if(eEvent == OMX_EventCmdComplete) { + if (Data1 == OMX_CommandStateSet) { + appPriv->state = (int)Data2; + printf( "State changed in "); + switch ((int)Data2) { + case OMX_StateInvalid: + printf( "OMX_StateInvalid\n"); + break; + case OMX_StateLoaded: + printf( "OMX_StateLoaded\n"); + break; + case OMX_StateIdle: + printf( "OMX_StateIdle\n"); + break; + case OMX_StateExecuting: + printf( "OMX_StateExecuting\n"); + break; + case OMX_StatePause: + printf( "OMX_StatePause\n"); + break; + case OMX_StateWaitForResources: + printf( "OMX_StateWaitForResources\n"); + break; + default: + printf( "default\n"); + break; + + } + //tsem_up(appPriv->decoderEventSem); + tsem_up(appPriv->stateSem); + printf( "tsem up decoderEventSem\n"); + } else if (OMX_CommandPortEnable || OMX_CommandPortDisable) { + printf( "In %s Received Port Enable/Disable Event\n",__func__); + tsem_up(appPriv->decoderEventSem); + printf( "tsem up decoderEventSem\n"); + } + } else if(eEvent == OMX_EventPortSettingsChanged) { + printf( "\n port settings change event handler in %s %i\n", __func__, Data2); + if(Data2 == 0) { + +/* + if(!flagSetupTunnel) { + err = setPortParameters(); + printf("set port param %i\n",err); + printf("tsem_up\n"); + tsem_up(appPriv->decoderEventSem); + if(flagIsColorConvRequested == 1) { + pOutBufferColorConv1 = pOutBufferColorConv2 = NULL; + err = OMX_SendCommand(appPriv->colorconv_handle, OMX_CommandStateSet, OMX_StateIdle, NULL); +#if 1 + err = OMX_UseBuffer(appPriv->colorconv_handle, &pInBufferColorConv1, 0, NULL, buffer_out_size, pOutBuffer1->pBuffer); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to use the video dec comp allocate buffer\n"); + exit(1); + } + err = OMX_UseBuffer(appPriv->colorconv_handle, &pInBufferColorConv2, 0, NULL, buffer_out_size, pOutBuffer2->pBuffer); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to use the video dec comp allocate buffer\n"); + exit(1); + } +#else + err = OMX_UseBuffer(appPriv->colorconv_handle, &pInBufferColorConv1, 0, NULL, buffer_out_size, pOutBuffer1->data[0]); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to use the video dec comp allocate buffer\n"); + exit(1); + } + err = OMX_UseBuffer(appPriv->colorconv_handle, &pInBufferColorConv2, 0, NULL, buffer_out_size, pOutBuffer2->data[0]); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to use the video dec comp allocate buffer\n"); + exit(1); + } +#endif + omx_colorconvPortDefinition.nPortIndex = 1; + setHeader(&omx_colorconvPortDefinition, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + err = OMX_GetParameter(appPriv->colorconv_handle, OMX_IndexParamPortDefinition, &omx_colorconvPortDefinition); + outbuf_colorconv_size = omx_colorconvPortDefinition.nBufferSize; + printf( " outbuf_colorconv_size : %d \n", (int)outbuf_colorconv_size); + + err = OMX_AllocateBuffer(appPriv->colorconv_handle, &pOutBufferColorConv1, 1, NULL, outbuf_colorconv_size); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to allocate buffer in color conv\n"); + exit(1); + } + err = OMX_AllocateBuffer(appPriv->colorconv_handle, &pOutBufferColorConv2, 1, NULL, outbuf_colorconv_size); + if(err != OMX_ErrorNone) { + printf(DEB_LEV_ERR, "Unable to allocate buffer in colro conv\n"); + exit(1); + } + + printf( "Before locking on idle wait semaphore\n"); + tsem_down(appPriv->colorconvEventSem); + printf( "color conv Sem free\n"); + + } + } + */ + } + } else if(eEvent == OMX_EventBufferFlag) { + printf( "In %s OMX_BUFFERFLAG_EOS\n", __func__); + if((int)Data2 == OMX_BUFFERFLAG_EOS) { + tsem_up(appPriv->eofSem); + } + } else { + printf( "Param1 is %x\n", (int)Data1); + printf( "Param2 is %x\n", (int)Data2); + } + + return err; +} + +OMX_ERRORTYPE videodecEmptyBufferDone( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) { + + OMX_ERRORTYPE err; +/* + int data_read; + static int iBufferDropped=0; + unsigned char *pTmp; + int offset = 0; + + printf( "Hi there, I am in the %s callback.\n", __func__); + + pthread_mutex_lock(&mutex); + + pTmp = pBuffer->pBuffer; + pTmp[offset++] = 0; + pTmp[offset++] = 0; + pTmp[offset++] = 0; + pTmp[offset++] = 1; + + data_read = FromByteStream2NalUnit(fd, pTmp+offset); + usleep(3000); + pBuffer->nFilledLen = data_read+offset; + pBuffer->nOffset = 0; + pthread_mutex_unlock(&mutex); + + if (data_read <= 0) { + printf( "In the %s no more input data available\n", __func__); + iBufferDropped++; + if(iBufferDropped>=2) { + bEOS=OMX_TRUE; + return OMX_ErrorNone; + } + pBuffer->nFilledLen=0; + pBuffer->nFlags = OMX_BUFFERFLAG_EOS; + memset(pBuffer->pBuffer, 0x0, 640*480); + err = OMX_EmptyThisBuffer(hComponent, pBuffer); + printf( "EmptyThisBuffer for EOS %d\n", err); + hex_dump("src2", pBuffer->pBuffer, 16); + return OMX_ErrorNone; + } + + if(!bEOS) { + printf( "EmptyThisBuffer %x, %d\n", (int)pBuffer, offset); + err = OMX_EmptyThisBuffer(hComponent, pBuffer); + hex_dump("src1", pBuffer->pBuffer, 16); + } else { + printf( "In %s Dropping Empty This buffer to Audio Dec\n", __func__); + } +*/ + return OMX_ErrorNone; +} + + +OMX_ERRORTYPE videodecFillBufferDone( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) { + + OMX_ERRORTYPE err; +/* + OMX_STATETYPE eState; + + printf( "Hi there, I am in the %s callback.\n", __func__); + if(pBuffer != NULL) { + if(!bEOS) { + if(flagIsColorConvRequested && (!flagSetupTunnel)) { + OMX_GetState(appPriv->colorconv_handle,&eState); + if(eState == OMX_StateExecuting || eState == OMX_StatePause) { + if(pInBufferColorConv1->pBuffer == pBuffer->pBuffer) { + pInBufferColorConv1->nFilledLen = pBuffer->nFilledLen; + err = OMX_EmptyThisBuffer(appPriv->colorconv_handle, pInBufferColorConv1); + } else { + pInBufferColorConv2->nFilledLen = pBuffer->nFilledLen; + err = OMX_EmptyThisBuffer(appPriv->colorconv_handle, pInBufferColorConv2); + } + if(err != OMX_ErrorNone) { + printf( "In %s Error %08x Calling FillThisBuffer\n", __func__,err); + } + } else { + err = OMX_FillThisBuffer(hComponent, pBuffer); + } + } else if((pBuffer->nFilledLen > 0) && (!flagSetupTunnel)) { + printf( "nFilledLen :%d \n", pBuffer->nFilledLen); + //fwrite(pBuffer->pBuffer, 1, pBuffer->nFilledLen, outfile); + decoder_output_dump(pBuffer->pBuffer); + pBuffer->nFilledLen = 0; + } else { + printf( "In %s Empty buffer in Else\n", __func__); + printf( "nFilledLen :%d \n", pBuffer->nFilledLen); + } + if(pBuffer->nFlags == OMX_BUFFERFLAG_EOS) { + printf( "In %s: eos=%x Calling Empty This Buffer\n", __func__, (int)pBuffer->nFlags); + bEOS = OMX_TRUE; + } + if(!bEOS && !flagIsColorConvRequested && (!flagSetupTunnel)) { + err = OMX_FillThisBuffer(hComponent, pBuffer); + printf( "FillThisBuffer :%d \n", err); + } + } else { + printf( "In %s: eos=%x Dropping Empty This Buffer\n", __func__,(int)pBuffer->nFlags); + } + } else { + printf( "Ouch! In %s: had NULL buffer to output...\n", __func__); + } +*/ + return OMX_ErrorNone; +} diff --git a/host_applications/linux/apps/hello_pi/hello_video/video.h b/host_applications/linux/apps/hello_pi/hello_video/video.h new file mode 100755 index 0000000..056db03 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_video/video.h @@ -0,0 +1,40 @@ +#include +#include +#include + +#include "queue.h" +#include "bcm_host.h" +#include "ilclient.h" +#include +#include +#include +#include "tsemaphore.h" + +#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) + +typedef struct appPrivateType{ + tsem_t* stateSem; + tsem_t* decoderEventSem; + tsem_t* eofSem; + OMX_HANDLETYPE videodechandle; + OMX_STATETYPE state; +} appPrivateType; + +/* Callback prototypes for video decoder */ +OMX_ERRORTYPE videodecEventHandler( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_EVENTTYPE eEvent, + OMX_OUT OMX_U32 Data1, + OMX_OUT OMX_U32 Data2, + OMX_OUT OMX_PTR pEventData); + +OMX_ERRORTYPE videodecEmptyBufferDone( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); + +OMX_ERRORTYPE videodecFillBufferDone( + OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_videocube/CMakeLists.txt new file mode 100755 index 0000000..d7fb059 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_videocube.bin) +set(SRCS triangle.c video.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/Makefile b/host_applications/linux/apps/hello_pi/hello_videocube/Makefile new file mode 100755 index 0000000..60d4b0a --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/Makefile @@ -0,0 +1,7 @@ +OBJS=triangle.o video.o +BIN=hello_videocube.bin +LDFLAGS+=-lilclient + +include ../Makefile.include + + diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/README.md b/host_applications/linux/apps/hello_pi/hello_videocube/README.md new file mode 100755 index 0000000..36fafcf --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/README.md @@ -0,0 +1,4 @@ +hello_videocube +=============== + +Sample for Raspberry Pi that uses egl_render to display video on an animated cube. \ No newline at end of file diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/cube_texture_and_coords.h b/host_applications/linux/apps/hello_pi/hello_videocube/cube_texture_and_coords.h new file mode 100755 index 0000000..7dd30a9 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/cube_texture_and_coords.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Spatial coordinates for the cube + +static const GLbyte quadx[6*4*3] = { + /* FRONT */ + -10, -10, 10, + 10, -10, 10, + -10, 10, 10, + 10, 10, 10, + + /* BACK */ + -10, -10, -10, + -10, 10, -10, + 10, -10, -10, + 10, 10, -10, + + /* LEFT */ + -10, -10, 10, + -10, 10, 10, + -10, -10, -10, + -10, 10, -10, + + /* RIGHT */ + 10, -10, -10, + 10, 10, -10, + 10, -10, 10, + 10, 10, 10, + + /* TOP */ + -10, 10, 10, + 10, 10, 10, + -10, 10, -10, + 10, 10, -10, + + /* BOTTOM */ + -10, -10, 10, + -10, -10, -10, + 10, -10, 10, + 10, -10, -10, +}; + +/** Texture coordinates for the quad. */ +static const GLfloat texCoords[6 * 4 * 2] = { + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f, + + 0.f, 0.f, + 0.f, 1.f, + 1.f, 0.f, + 1.f, 1.f +}; + diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/triangle.c b/host_applications/linux/apps/hello_pi/hello_videocube/triangle.c new file mode 100755 index 0000000..42a78a4 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/triangle.c @@ -0,0 +1,481 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, OtherCrashOverride +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// A rotating cube rendered with OpenGL|ES. Three images used as textures on the cube faces. + +#include +#include +#include +#include +#include +#include + +#include "bcm_host.h" + +#include "GLES/gl.h" +#include "EGL/egl.h" +#include "EGL/eglext.h" + +#include "cube_texture_and_coords.h" + +#include "triangle.h" +#include + + +#define PATH "./" + +#define IMAGE_SIZE_WIDTH 1920 +#define IMAGE_SIZE_HEIGHT 1080 + +#ifndef M_PI + #define M_PI 3.141592654 +#endif + + +typedef struct +{ + uint32_t screen_width; + uint32_t screen_height; +// OpenGL|ES objects + EGLDisplay display; + EGLSurface surface; + EGLContext context; + GLuint tex; +// model rotation vector and direction + GLfloat rot_angle_x_inc; + GLfloat rot_angle_y_inc; + GLfloat rot_angle_z_inc; +// current model rotation angles + GLfloat rot_angle_x; + GLfloat rot_angle_y; + GLfloat rot_angle_z; +// current distance from camera + GLfloat distance; + GLfloat distance_inc; +} CUBE_STATE_T; + +static void init_ogl(CUBE_STATE_T *state); +static void init_model_proj(CUBE_STATE_T *state); +static void reset_model(CUBE_STATE_T *state); +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); +static void redraw_scene(CUBE_STATE_T *state); +static void update_model(CUBE_STATE_T *state); +static void init_textures(CUBE_STATE_T *state); +static void exit_func(void); +static volatile int terminate; +static CUBE_STATE_T _state, *state=&_state; + +static void* eglImage = 0; +static pthread_t thread1; + + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void init_ogl(CUBE_STATE_T *state) +{ + int32_t success = 0; + EGLBoolean result; + EGLint num_config; + + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_ELEMENT_HANDLE_T dispman_element; + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + //EGL_SAMPLES, 4, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + EGLConfig config; + + // get an EGL display connection + state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + assert(state->display!=EGL_NO_DISPLAY); + + // initialize the EGL display connection + result = eglInitialize(state->display, NULL, NULL); + assert(EGL_FALSE != result); + + // get an appropriate EGL frame buffer configuration + // this uses a BRCM extension that gets the closest match, rather than standard which returns anything that matches + result = eglSaneChooseConfigBRCM(state->display, attribute_list, &config, 1, &num_config); + assert(EGL_FALSE != result); + + // create an EGL rendering context + state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); + assert(state->context!=EGL_NO_CONTEXT); + + // create an EGL window surface + success = graphics_get_display_size(0 /* LCD */, &state->screen_width, &state->screen_height); + assert( success >= 0 ); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = state->screen_width; + dst_rect.height = state->screen_height; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = state->screen_width << 16; + src_rect.height = state->screen_height << 16; + + dispman_display = vc_dispmanx_display_open( 0 /* LCD */); + dispman_update = vc_dispmanx_update_start( 0 ); + + dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display, + 0/*layer*/, &dst_rect, 0/*src*/, + &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/); + + nativewindow.element = dispman_element; + nativewindow.width = state->screen_width; + nativewindow.height = state->screen_height; + vc_dispmanx_update_submit_sync( dispman_update ); + + state->surface = eglCreateWindowSurface( state->display, config, &nativewindow, NULL ); + assert(state->surface != EGL_NO_SURFACE); + + // connect the context to the surface + result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); + assert(EGL_FALSE != result); + + // Set background color and clear buffers + glClearColor(0.15f, 0.25f, 0.35f, 1.0f); + + // Enable back face culling. + glEnable(GL_CULL_FACE); + + glMatrixMode(GL_MODELVIEW); +} + +/*********************************************************** + * Name: init_model_proj + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Sets the OpenGL|ES model to default values + * + * Returns: void + * + ***********************************************************/ +static void init_model_proj(CUBE_STATE_T *state) +{ + float nearp = 1.0f; + float farp = 500.0f; + float hht; + float hwd; + + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + + glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); + hwd = hht * (float)state->screen_width / (float)state->screen_height; + + glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); + + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( 3, GL_BYTE, 0, quadx ); + + reset_model(state); +} + +/*********************************************************** + * Name: reset_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Resets the Model projection and rotation direction + * + * Returns: void + * + ***********************************************************/ +static void reset_model(CUBE_STATE_T *state) +{ + // reset model position + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.f, 0.f, -50.f); + + // reset model rotation + state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; + state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; + state->distance = 40.f; +} + +/*********************************************************** + * Name: update_model + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Updates model projection to current position/rotation + * + * Returns: void + * + ***********************************************************/ +static void update_model(CUBE_STATE_T *state) +{ + // update position + state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); + state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); + state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); + state->distance = inc_and_clip_distance(state->distance, state->distance_inc); + + glLoadIdentity(); + // move camera back to see the cube + glTranslatef(0.f, 0.f, -state->distance); + + // Rotate model to new position + glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); + glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); + glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); +} + +/*********************************************************** + * Name: inc_and_wrap_angle + * + * Arguments: + * GLfloat angle current angle + * GLfloat angle_inc angle increment + * + * Description: Increments or decrements angle by angle_inc degrees + * Wraps to 0 at 360 deg. + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) +{ + angle += angle_inc; + + if (angle >= 360.0) + angle -= 360.f; + else if (angle <=0) + angle += 360.f; + + return angle; +} + +/*********************************************************** + * Name: inc_and_clip_distance + * + * Arguments: + * GLfloat distance current distance + * GLfloat distance_inc distance increment + * + * Description: Increments or decrements distance by distance_inc units + * Clips to range + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) +{ + distance += distance_inc; + + if (distance >= 120.0f) + distance = 120.f; + else if (distance <= 40.0f) + distance = 40.0f; + + return distance; +} + +/*********************************************************** + * Name: redraw_scene + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Draws the model and calls eglSwapBuffers + * to render to screen + * + * Returns: void + * + ***********************************************************/ +static void redraw_scene(CUBE_STATE_T *state) +{ + // Start with a clear screen + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // Need to rotate textures - do this by rotating each cube face + glRotatef(270.f, 0.f, 0.f, 1.f ); // front face normal along z axis + + // draw first 4 vertices + glDrawArrays( GL_TRIANGLE_STRIP, 0, 4); + + // same pattern for other 5 faces - rotation chosen to make image orientation 'nice' + glRotatef(90.f, 0.f, 0.f, 1.f ); // back face normal along z axis + glDrawArrays( GL_TRIANGLE_STRIP, 4, 4); + + glRotatef(90.f, 1.f, 0.f, 0.f ); // left face normal along x axis + glDrawArrays( GL_TRIANGLE_STRIP, 8, 4); + + glRotatef(90.f, 1.f, 0.f, 0.f ); // right face normal along x axis + glDrawArrays( GL_TRIANGLE_STRIP, 12, 4); + + glRotatef(270.f, 0.f, 1.f, 0.f ); // top face normal along y axis + glDrawArrays( GL_TRIANGLE_STRIP, 16, 4); + + glRotatef(90.f, 0.f, 1.f, 0.f ); // bottom face normal along y axis + glDrawArrays( GL_TRIANGLE_STRIP, 20, 4); + + eglSwapBuffers(state->display, state->surface); +} + +/*********************************************************** + * Name: init_textures + * + * Arguments: + * CUBE_STATE_T *state - holds OGLES model info + * + * Description: Initialise OGL|ES texture surfaces to use image + * buffers + * + * Returns: void + * + ***********************************************************/ +static void init_textures(CUBE_STATE_T *state) +{ + //// load three texture buffers but use them on six OGL|ES texture surfaces + glGenTextures(1, &state->tex); + + glBindTexture(GL_TEXTURE_2D, state->tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE_WIDTH, IMAGE_SIZE_HEIGHT, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + + /* Create EGL Image */ + eglImage = eglCreateImageKHR( + state->display, + state->context, + EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer)state->tex, + 0); + + if (eglImage == EGL_NO_IMAGE_KHR) + { + printf("eglCreateImageKHR failed.\n"); + exit(1); + } + + // Start rendering + pthread_create(&thread1, NULL, video_decode_test, eglImage); + + // setup overall texture environment + glTexCoordPointer(2, GL_FLOAT, 0, texCoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glEnable(GL_TEXTURE_2D); + + // Bind texture surface to current vertices + glBindTexture(GL_TEXTURE_2D, state->tex); +} +//------------------------------------------------------------------------------ + +static void exit_func(void) +// Function to be passed to atexit(). +{ + if (eglImage != 0) + { + if (!eglDestroyImageKHR(state->display, (EGLImageKHR) eglImage)) + printf("eglDestroyImageKHR failed."); + } + + // clear screen + glClear( GL_COLOR_BUFFER_BIT ); + eglSwapBuffers(state->display, state->surface); + + // Release OpenGL resources + eglMakeCurrent( state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + eglDestroySurface( state->display, state->surface ); + eglDestroyContext( state->display, state->context ); + eglTerminate( state->display ); + + printf("\ncube closed\n"); +} // exit_func() + +//============================================================================== + +int main () +{ + bcm_host_init(); + printf("Note: ensure you have sufficient gpu_mem configured\n"); + + // Clear application state + memset( state, 0, sizeof( *state ) ); + + // Start OGLES + init_ogl(state); + + // Setup the model world + init_model_proj(state); + + // initialise the OGLES texture(s) + init_textures(state); + + while (!terminate) + { + update_model(state); + redraw_scene(state); + } + exit_func(); + return 0; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/triangle.h b/host_applications/linux/apps/hello_pi/hello_videocube/triangle.h new file mode 100755 index 0000000..0f50e93 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/triangle.h @@ -0,0 +1,30 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#pragma once + + +void* video_decode_test(void* arg); diff --git a/host_applications/linux/apps/hello_pi/hello_videocube/video.c b/host_applications/linux/apps/hello_pi/hello_videocube/video.c new file mode 100755 index 0000000..4baae8f --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_videocube/video.c @@ -0,0 +1,266 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, OtherCrashOverride +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Video decode demo using OpenMAX IL though the ilcient helper library + +#include +#include +#include + +#include "bcm_host.h" +#include "ilclient.h" + +static OMX_BUFFERHEADERTYPE* eglBuffer = NULL; +static COMPONENT_T* egl_render = NULL; + +static void* eglImage = 0; + +void my_fill_buffer_done(void* data, COMPONENT_T* comp) +{ + if (OMX_FillThisBuffer(ilclient_get_handle(egl_render), eglBuffer) != OMX_ErrorNone) + { + printf("OMX_FillThisBuffer failed in callback\n"); + exit(1); + } +} + + +// Modified function prototype to work with pthreads +void *video_decode_test(void* arg) +{ + const char* filename = "/opt/vc/src/hello_pi/hello_video/test.h264"; + eglImage = arg; + + if (eglImage == 0) + { + printf("eglImage is null.\n"); + exit(1); + } + + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; + COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *clock = NULL; + COMPONENT_T *list[5]; + TUNNEL_T tunnel[4]; + ILCLIENT_T *client; + FILE *in; + int status = 0; + unsigned int data_len = 0; + + memset(list, 0, sizeof(list)); + memset(tunnel, 0, sizeof(tunnel)); + + if((in = fopen(filename, "rb")) == NULL) + return (void *)-2; + + if((client = ilclient_init()) == NULL) + { + fclose(in); + return (void *)-3; + } + + if(OMX_Init() != OMX_ErrorNone) + { + ilclient_destroy(client); + fclose(in); + return (void *)-4; + } + + // callback + ilclient_set_fill_buffer_done_callback(client, my_fill_buffer_done, 0); + + // create video_decode + if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) + status = -14; + list[0] = video_decode; + + // create egl_render + if(status == 0 && ilclient_create_component(client, &egl_render, "egl_render", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_OUTPUT_BUFFERS) != 0) + status = -14; + list[1] = egl_render; + + // create clock + if(status == 0 && ilclient_create_component(client, &clock, "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[2] = clock; + + memset(&cstate, 0, sizeof(cstate)); + cstate.nSize = sizeof(cstate); + cstate.nVersion.nVersion = OMX_VERSION; + cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; + cstate.nWaitMask = 1; + if(clock != NULL && OMX_SetParameter(ILC_GET_HANDLE(clock), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) + status = -13; + + // create video_scheduler + if(status == 0 && ilclient_create_component(client, &video_scheduler, "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0) + status = -14; + list[3] = video_scheduler; + + set_tunnel(tunnel, video_decode, 131, video_scheduler, 10); + set_tunnel(tunnel+1, video_scheduler, 11, egl_render, 220); + set_tunnel(tunnel+2, clock, 80, video_scheduler, 12); + + // setup clock tunnel first + if(status == 0 && ilclient_setup_tunnel(tunnel+2, 0, 0) != 0) + status = -15; + else + ilclient_change_component_state(clock, OMX_StateExecuting); + + if(status == 0) + ilclient_change_component_state(video_decode, OMX_StateIdle); + + memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE)); + format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nVersion.nVersion = OMX_VERSION; + format.nPortIndex = 130; + format.eCompressionFormat = OMX_VIDEO_CodingAVC; + + if(status == 0 && + OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone && + ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) + { + OMX_BUFFERHEADERTYPE *buf; + int port_settings_changed = 0; + int first_packet = 1; + + ilclient_change_component_state(video_decode, OMX_StateExecuting); + + while((buf = ilclient_get_input_buffer(video_decode, 130, 1)) != NULL) + { + // feed data and wait until we get port settings changed + unsigned char *dest = buf->pBuffer; + + // loop if at end + if (feof(in)) + rewind(in); + + data_len += fread(dest, 1, buf->nAllocLen-data_len, in); + + if(port_settings_changed == 0 && + ((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) || + (data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1, + ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) + { + port_settings_changed = 1; + + if(ilclient_setup_tunnel(tunnel, 0, 0) != 0) + { + status = -7; + break; + } + + ilclient_change_component_state(video_scheduler, OMX_StateExecuting); + + // now setup tunnel to egl_render + if(ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0) + { + status = -12; + break; + } + + // Set egl_render to idle + ilclient_change_component_state(egl_render, OMX_StateIdle); + + // Enable the output port and tell egl_render to use the texture as a buffer + //ilclient_enable_port(egl_render, 221); THIS BLOCKS SO CAN'T BE USED + if (OMX_SendCommand(ILC_GET_HANDLE(egl_render), OMX_CommandPortEnable, 221, NULL) != OMX_ErrorNone) + { + printf("OMX_CommandPortEnable failed.\n"); + exit(1); + } + + if (OMX_UseEGLImage(ILC_GET_HANDLE(egl_render), &eglBuffer, 221, NULL, eglImage) != OMX_ErrorNone) + { + printf("OMX_UseEGLImage failed.\n"); + exit(1); + } + + // Set egl_render to executing + ilclient_change_component_state(egl_render, OMX_StateExecuting); + + + // Request egl_render to write data to the texture buffer + if(OMX_FillThisBuffer(ILC_GET_HANDLE(egl_render), eglBuffer) != OMX_ErrorNone) + { + printf("OMX_FillThisBuffer failed.\n"); + exit(1); + } + } + if(!data_len) + break; + + buf->nFilledLen = data_len; + data_len = 0; + + buf->nOffset = 0; + if(first_packet) + { + buf->nFlags = OMX_BUFFERFLAG_STARTTIME; + first_packet = 0; + } + else + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + { + status = -6; + break; + } + } + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone) + status = -20; + + // need to flush the renderer to allow video_decode to disable its input port + ilclient_flush_tunnels(tunnel, 0); + + ilclient_disable_port_buffers(video_decode, 130, NULL, NULL, NULL); + } + + fclose(in); + + ilclient_disable_tunnel(tunnel); + ilclient_disable_tunnel(tunnel+1); + ilclient_disable_tunnel(tunnel+2); + ilclient_teardown_tunnels(tunnel); + + ilclient_state_transition(list, OMX_StateIdle); + ilclient_state_transition(list, OMX_StateLoaded); + + ilclient_cleanup_components(list); + + OMX_Deinit(); + + ilclient_destroy(client); + return (void *)status; +} + diff --git a/host_applications/linux/apps/hello_pi/hello_world/CMakeLists.txt b/host_applications/linux/apps/hello_pi/hello_world/CMakeLists.txt new file mode 100755 index 0000000..b0120fe --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_world/CMakeLists.txt @@ -0,0 +1,8 @@ +set(EXEC hello_world.bin) +set(SRCS world.c) + +add_executable(${EXEC} ${SRCS}) +target_link_libraries(${EXEC} ${HELLO_PI_LIBS}) + +install(TARGETS ${EXEC} + RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/hello_pi/hello_world/Makefile b/host_applications/linux/apps/hello_pi/hello_world/Makefile new file mode 100755 index 0000000..7bf2402 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_world/Makefile @@ -0,0 +1,5 @@ +OBJS=world.o +BIN=hello_world.bin + +include ../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/hello_world/world.c b/host_applications/linux/apps/hello_pi/hello_world/world.c new file mode 100755 index 0000000..0a9c4aa --- /dev/null +++ b/host_applications/linux/apps/hello_pi/hello_world/world.c @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Classic Hello World + +#include + +int main(void) +{ + printf("Hello world!\n"); + return 0; +} diff --git a/host_applications/linux/apps/hello_pi/libs/ilclient/Makefile b/host_applications/linux/apps/hello_pi/libs/ilclient/Makefile new file mode 100755 index 0000000..82979e3 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/ilclient/Makefile @@ -0,0 +1,5 @@ +OBJS=ilclient.o ilcore.o +LIB=libilclient.a + +include ../../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/libs/ilclient/ilclient.c b/host_applications/linux/apps/hello_pi/libs/ilclient/ilclient.c new file mode 100755 index 0000000..da08ad0 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/ilclient/ilclient.c @@ -0,0 +1,1836 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file + * + * \brief This API defines helper functions for writing IL clients. + * + * This file defines an IL client side library. This is useful when + * writing IL clients, since there tends to be much repeated and + * common code across both single and multiple clients. This library + * seeks to remove that common code and abstract some of the + * interactions with components. There is a wrapper around a + * component and tunnel, and some operations can be done on lists of + * these. The callbacks from components are handled, and specific + * events can be checked or waited for. +*/ + +#include +#include +#include +#include +#include +#include + +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_logging.h" +#include "interface/vmcs_host/vchost.h" + +#include "IL/OMX_Broadcom.h" +#include "ilclient.h" + +#define VCOS_LOG_CATEGORY (&ilclient_log_category) + +#ifndef ILCLIENT_THREAD_DEFAULT_STACK_SIZE +#define ILCLIENT_THREAD_DEFAULT_STACK_SIZE (6<<10) +#endif + +static VCOS_LOG_CAT_T ilclient_log_category; + +/****************************************************************************** +Static data and types used only in this file. +******************************************************************************/ + +struct _ILEVENT_T { + OMX_EVENTTYPE eEvent; + OMX_U32 nData1; + OMX_U32 nData2; + OMX_PTR pEventData; + struct _ILEVENT_T *next; +}; + +#define NUM_EVENTS 100 +struct _ILCLIENT_T { + ILEVENT_T *event_list; + VCOS_SEMAPHORE_T event_sema; + ILEVENT_T event_rep[NUM_EVENTS]; + + ILCLIENT_CALLBACK_T port_settings_callback; + void *port_settings_callback_data; + ILCLIENT_CALLBACK_T eos_callback; + void *eos_callback_data; + ILCLIENT_CALLBACK_T error_callback; + void *error_callback_data; + ILCLIENT_BUFFER_CALLBACK_T fill_buffer_done_callback; + void *fill_buffer_done_callback_data; + ILCLIENT_BUFFER_CALLBACK_T empty_buffer_done_callback; + void *empty_buffer_done_callback_data; + ILCLIENT_CALLBACK_T configchanged_callback; + void *configchanged_callback_data; +}; + +struct _COMPONENT_T { + OMX_HANDLETYPE comp; + ILCLIENT_CREATE_FLAGS_T flags; + VCOS_SEMAPHORE_T sema; + VCOS_EVENT_FLAGS_T event; + struct _COMPONENT_T *related; + OMX_BUFFERHEADERTYPE *out_list; + OMX_BUFFERHEADERTYPE *in_list; + char name[32]; + char bufname[32]; + unsigned int error_mask; + unsigned int private; + ILEVENT_T *list; + ILCLIENT_T *client; +}; + +#define random_wait() +static char *states[] = {"Invalid", "Loaded", "Idle", "Executing", "Pause", "WaitingForResources"}; + +typedef enum { + ILCLIENT_ERROR_UNPOPULATED = 0x1, + ILCLIENT_ERROR_SAMESTATE = 0x2, + ILCLIENT_ERROR_BADPARAMETER = 0x4 +} ILERROR_MASK_T; + +/****************************************************************************** +Static functions. +******************************************************************************/ + +static OMX_ERRORTYPE ilclient_empty_buffer_done(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_empty_buffer_done_error(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_fill_buffer_done(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_fill_buffer_done_error(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_event_handler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, + OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData); +static void ilclient_lock_events(ILCLIENT_T *st); +static void ilclient_unlock_events(ILCLIENT_T *st); + +/****************************************************************************** +Global functions +******************************************************************************/ + +/*********************************************************** + * Name: ilclient_init + * + * Description: Creates ilclient pointer + * + * Returns: pointer to client structure + ***********************************************************/ +ILCLIENT_T *ilclient_init() +{ + ILCLIENT_T *st = vcos_malloc(sizeof(ILCLIENT_T), "ilclient"); + int i; + + if (!st) + return NULL; + + vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_WARN); + vcos_log_register("ilclient", VCOS_LOG_CATEGORY); + + memset(st, 0, sizeof(ILCLIENT_T)); + + i = vcos_semaphore_create(&st->event_sema, "il:event", 1); + vc_assert(i == VCOS_SUCCESS); + + ilclient_lock_events(st); + st->event_list = NULL; + for (i=0; ievent_rep[i].eEvent = -1; // mark as unused + st->event_rep[i].next = st->event_list; + st->event_list = st->event_rep+i; + } + ilclient_unlock_events(st); + return st; +} + +/*********************************************************** + * Name: ilclient_destroy + * + * Description: frees client state + * + * Returns: void + ***********************************************************/ +void ilclient_destroy(ILCLIENT_T *st) +{ + vcos_semaphore_delete(&st->event_sema); + vcos_free(st); + vcos_log_unregister(VCOS_LOG_CATEGORY); +} + +/*********************************************************** + * Name: ilclient_set_port_settings_callback + * + * Description: sets the callback used when receiving port settings + * changed messages. The data field in the callback function will be + * the port index reporting the message. + * + * Returns: void + ***********************************************************/ +void ilclient_set_port_settings_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->port_settings_callback = func; + st->port_settings_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_eos_callback + * + * Description: sets the callback used when receiving eos flags. The + * data parameter in the callback function will be the port index + * reporting an eos flag. + * + * Returns: void + ***********************************************************/ +void ilclient_set_eos_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->eos_callback = func; + st->eos_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_error_callback + * + * Description: sets the callback used when receiving error events. + * The data parameter in the callback function will be the error code + * being reported. + * + * Returns: void + ***********************************************************/ +void ilclient_set_error_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->error_callback = func; + st->error_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_fill_buffer_done_callback + * + * Description: sets the callback used when receiving + * fill_buffer_done event + * + * Returns: void + ***********************************************************/ +void ilclient_set_fill_buffer_done_callback(ILCLIENT_T *st, ILCLIENT_BUFFER_CALLBACK_T func, void *userdata) +{ + st->fill_buffer_done_callback = func; + st->fill_buffer_done_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_empty_buffer_done_callback + * + * Description: sets the callback used when receiving + * empty_buffer_done event + * + * Returns: void + ***********************************************************/ +void ilclient_set_empty_buffer_done_callback(ILCLIENT_T *st, ILCLIENT_BUFFER_CALLBACK_T func, void *userdata) +{ + st->empty_buffer_done_callback = func; + st->empty_buffer_done_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_configchanged_callback + * + * Description: sets the callback used when a config changed + * event is received + * + * Returns: void + ***********************************************************/ +void ilclient_set_configchanged_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->configchanged_callback = func; + st->configchanged_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_create_component + * + * Description: initialises a component state structure and creates + * the IL component. + * + * Returns: 0 on success, -1 on failure + ***********************************************************/ +int ilclient_create_component(ILCLIENT_T *client, COMPONENT_T **comp, char *name, + ILCLIENT_CREATE_FLAGS_T flags) +{ + OMX_CALLBACKTYPE callbacks; + OMX_ERRORTYPE error; + char component_name[128]; + int32_t status; + + *comp = vcos_malloc(sizeof(COMPONENT_T), "il:comp"); + if(!*comp) + return -1; + + memset(*comp, 0, sizeof(COMPONENT_T)); + +#define COMP_PREFIX "OMX.broadcom." + + status = vcos_event_flags_create(&(*comp)->event,"il:comp"); + vc_assert(status == VCOS_SUCCESS); + status = vcos_semaphore_create(&(*comp)->sema, "il:comp", 1); + vc_assert(status == VCOS_SUCCESS); + (*comp)->client = client; + + vcos_snprintf((*comp)->name, sizeof((*comp)->name), "cl:%s", name); + vcos_snprintf((*comp)->bufname, sizeof((*comp)->bufname), "cl:%s buffer", name); + vcos_snprintf(component_name, sizeof(component_name), "%s%s", COMP_PREFIX, name); + + (*comp)->flags = flags; + + callbacks.EventHandler = ilclient_event_handler; + callbacks.EmptyBufferDone = flags & ILCLIENT_ENABLE_INPUT_BUFFERS ? ilclient_empty_buffer_done : ilclient_empty_buffer_done_error; + callbacks.FillBufferDone = flags & ILCLIENT_ENABLE_OUTPUT_BUFFERS ? ilclient_fill_buffer_done : ilclient_fill_buffer_done_error; + + error = OMX_GetHandle(&(*comp)->comp, component_name, *comp, &callbacks); + + if (error == OMX_ErrorNone) + { + OMX_UUIDTYPE uid; + char name[128]; + OMX_VERSIONTYPE compVersion, specVersion; + + if(OMX_GetComponentVersion((*comp)->comp, name, &compVersion, &specVersion, &uid) == OMX_ErrorNone) + { + char *p = (char *) uid + strlen(COMP_PREFIX); + + vcos_snprintf((*comp)->name, sizeof((*comp)->name), "cl:%s", p); + (*comp)->name[sizeof((*comp)->name)-1] = 0; + vcos_snprintf((*comp)->bufname, sizeof((*comp)->bufname), "cl:%s buffer", p); + (*comp)->bufname[sizeof((*comp)->bufname)-1] = 0; + } + + if(flags & (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_OUTPUT_ZERO_BUFFERS)) + { + OMX_PORT_PARAM_TYPE ports; + OMX_INDEXTYPE types[] = {OMX_IndexParamAudioInit, OMX_IndexParamVideoInit, OMX_IndexParamImageInit, OMX_IndexParamOtherInit}; + int i; + + ports.nSize = sizeof(OMX_PORT_PARAM_TYPE); + ports.nVersion.nVersion = OMX_VERSION; + + for(i=0; i<4; i++) + { + OMX_ERRORTYPE error = OMX_GetParameter((*comp)->comp, types[i], &ports); + if(error == OMX_ErrorNone) + { + uint32_t j; + for(j=0; jcomp, OMX_IndexParamPortDefinition, &portdef) == OMX_ErrorNone) + { + if(portdef.eDir == OMX_DirOutput && portdef.nBufferCountActual > 0) + { + portdef.nBufferCountActual = 0; + OMX_SetParameter((*comp)->comp, OMX_IndexParamPortDefinition, &portdef); + } + } + } + } + } + } + } + return 0; + } + else + { + vcos_event_flags_delete(&(*comp)->event); + vcos_semaphore_delete(&(*comp)->sema); + vcos_free(*comp); + *comp = NULL; + return -1; + } +} + +/*********************************************************** + * Name: ilclient_remove_event + * + * Description: Removes an event from a component event list. ignore1 + * and ignore2 are flags indicating whether to not match on nData1 and + * nData2 respectively. + * + * Returns: 0 if the event was removed. -1 if no matching event was + * found. + ***********************************************************/ +int ilclient_remove_event(COMPONENT_T *st, OMX_EVENTTYPE eEvent, + OMX_U32 nData1, int ignore1, OMX_IN OMX_U32 nData2, int ignore2) +{ + ILEVENT_T *cur, *prev; + uint32_t set; + ilclient_lock_events(st->client); + + cur = st->list; + prev = NULL; + + while (cur && !(cur->eEvent == eEvent && (ignore1 || cur->nData1 == nData1) && (ignore2 || cur->nData2 == nData2))) + { + prev = cur; + cur = cur->next; + } + + if (cur == NULL) + { + ilclient_unlock_events(st->client); + return -1; + } + + if (prev == NULL) + st->list = cur->next; + else + prev->next = cur->next; + + // add back into spare list + cur->next = st->client->event_list; + st->client->event_list = cur; + cur->eEvent = -1; // mark as unused + + // if we're removing an OMX_EventError or OMX_EventParamOrConfigChanged event, then clear the error bit from the eventgroup, + // since the user might have been notified through the error callback, and then + // can't clear the event bit - this will then cause problems the next time they + // wait for an error. + if(eEvent == OMX_EventError) + vcos_event_flags_get(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + else if(eEvent == OMX_EventParamOrConfigChanged) + vcos_event_flags_get(&st->event, ILCLIENT_CONFIG_CHANGED, VCOS_OR_CONSUME, 0, &set); + + ilclient_unlock_events(st->client); + return 0; +} + +/*********************************************************** + * Name: ilclient_state_transition + * + * Description: Transitions a null terminated list of IL components to + * a given state. All components are told to transition in a random + * order before any are checked for transition completion. + * + * Returns: void + ***********************************************************/ +void ilclient_state_transition(COMPONENT_T *list[], OMX_STATETYPE state) +{ + OMX_ERRORTYPE error; + int i, num; + uint32_t set; + + num=0; + while (list[num]) + num++; + + // if we transition the supplier port first, it will call freebuffer on the non + // supplier, which will correctly signal a port unpopulated error. We want to + // ignore these errors. + if (state == OMX_StateLoaded) + for (i=0; ierror_mask |= ILCLIENT_ERROR_UNPOPULATED; + for (i=0; iprivate = ((rand() >> 13) & 0xff)+1; + + for (i=0; iprivate && (min == -1 || list[min]->private > list[j]->private)) + min = j; + + list[min]->private = 0; + + random_wait(); + //Clear error event for this component + vcos_event_flags_get(&list[min]->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + + error = OMX_SendCommand(list[min]->comp, OMX_CommandStateSet, state, NULL); + vc_assert(error == OMX_ErrorNone); + } + + random_wait(); + + for (i=0; ierror_mask &= ~ILCLIENT_ERROR_UNPOPULATED; +} + +/*********************************************************** + * Name: ilclient_teardown_tunnels + * + * Description: tears down a null terminated list of tunnels. + * + * Returns: void + ***********************************************************/ +void ilclient_teardown_tunnels(TUNNEL_T *tunnel) +{ + int i; + OMX_ERRORTYPE error; + + i=0;; + while (tunnel[i].source) + { + error = OMX_SetupTunnel(tunnel[i].source->comp, tunnel[i].source_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SetupTunnel(tunnel[i].sink->comp, tunnel[i].sink_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + i++; + } +} + +/*********************************************************** + * Name: ilclient_disable_tunnel + * + * Description: disables a tunnel by disabling the ports. Allows + * ports to signal same state error if they were already disabled. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_tunnel(TUNNEL_T *tunnel) +{ + OMX_ERRORTYPE error; + + if(tunnel->source == 0 || tunnel->sink == 0) + return; + + tunnel->source->error_mask |= ILCLIENT_ERROR_UNPOPULATED; + tunnel->sink->error_mask |= ILCLIENT_ERROR_UNPOPULATED; + + error = OMX_SendCommand(tunnel->source->comp, OMX_CommandPortDisable, tunnel->source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel->sink->comp, OMX_CommandPortDisable, tunnel->sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + if(ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortDisable, tunnel->source_port) < 0) + vc_assert(0); + + if(ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortDisable, tunnel->sink_port) < 0) + vc_assert(0); + + tunnel->source->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED; + tunnel->sink->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED; +} + +/*********************************************************** + * Name: ilclient_enable_tunnel + * + * Description: enables a tunnel by enabling the ports + * + * Returns: 0 on success, -1 on failure + ***********************************************************/ +int ilclient_enable_tunnel(TUNNEL_T *tunnel) +{ + OMX_STATETYPE state; + OMX_ERRORTYPE error; + + ilclient_debug_output("ilclient: enable tunnel from %x/%d to %x/%d", + tunnel->source, tunnel->source_port, + tunnel->sink, tunnel->sink_port); + + error = OMX_SendCommand(tunnel->source->comp, OMX_CommandPortEnable, tunnel->source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel->sink->comp, OMX_CommandPortEnable, tunnel->sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + // to complete, the sink component can't be in loaded state + error = OMX_GetState(tunnel->sink->comp, &state); + vc_assert(error == OMX_ErrorNone); + if (state == OMX_StateLoaded) + { + int ret = 0; + + if(ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortEnable, tunnel->sink_port) != 0 || + OMX_SendCommand(tunnel->sink->comp, OMX_CommandStateSet, OMX_StateIdle, NULL) != OMX_ErrorNone || + (ret = ilclient_wait_for_command_complete_dual(tunnel->sink, OMX_CommandStateSet, OMX_StateIdle, tunnel->source)) < 0) + { + if(ret == -2) + { + // the error was reported fom the source component: clear this error and disable the sink component + ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortEnable, tunnel->source_port); + ilclient_disable_port(tunnel->sink, tunnel->sink_port); + } + + ilclient_debug_output("ilclient: could not change component state to IDLE"); + ilclient_disable_port(tunnel->source, tunnel->source_port); + return -1; + } + } + else + { + if (ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortEnable, tunnel->sink_port) != 0) + { + ilclient_debug_output("ilclient: could not change sink port %d to enabled", tunnel->sink_port); + + //Oops failed to enable the sink port + ilclient_disable_port(tunnel->source, tunnel->source_port); + //Clean up the port enable event from the source port. + ilclient_wait_for_event(tunnel->source, OMX_EventCmdComplete, + OMX_CommandPortEnable, 0, tunnel->source_port, 0, + ILCLIENT_PORT_ENABLED | ILCLIENT_EVENT_ERROR, VCOS_EVENT_FLAGS_SUSPEND); + return -1; + } + } + + if(ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortEnable, tunnel->source_port) != 0) + { + ilclient_debug_output("ilclient: could not change source port %d to enabled", tunnel->source_port); + + //Failed to enable the source port + ilclient_disable_port(tunnel->sink, tunnel->sink_port); + return -1; + } + + return 0; +} + + +/*********************************************************** + * Name: ilclient_flush_tunnels + * + * Description: flushes all ports used in a null terminated list of + * tunnels. max specifies the maximum number of tunnels to flush from + * the list, where max=0 means all tunnels. + * + * Returns: void + ***********************************************************/ +void ilclient_flush_tunnels(TUNNEL_T *tunnel, int max) +{ + OMX_ERRORTYPE error; + int i; + + i=0; + while (tunnel[i].source && (max == 0 || i < max)) + { + error = OMX_SendCommand(tunnel[i].source->comp, OMX_CommandFlush, tunnel[i].source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel[i].sink->comp, OMX_CommandFlush, tunnel[i].sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + ilclient_wait_for_event(tunnel[i].source, OMX_EventCmdComplete, + OMX_CommandFlush, 0, tunnel[i].source_port, 0, + ILCLIENT_PORT_FLUSH, VCOS_EVENT_FLAGS_SUSPEND); + ilclient_wait_for_event(tunnel[i].sink, OMX_EventCmdComplete, + OMX_CommandFlush, 0, tunnel[i].sink_port, 0, + ILCLIENT_PORT_FLUSH, VCOS_EVENT_FLAGS_SUSPEND); + i++; + } +} + + +/*********************************************************** + * Name: ilclient_return_events + * + * Description: Returns all events from a component event list to the + * list of unused event structures. + * + * Returns: void + ***********************************************************/ +void ilclient_return_events(COMPONENT_T *comp) +{ + ilclient_lock_events(comp->client); + while (comp->list) + { + ILEVENT_T *next = comp->list->next; + comp->list->next = comp->client->event_list; + comp->client->event_list = comp->list; + comp->list = next; + } + ilclient_unlock_events(comp->client); +} + +/*********************************************************** + * Name: ilclient_cleanup_components + * + * Description: frees all components from a null terminated list and + * deletes resources used in component state structure. + * + * Returns: void + ***********************************************************/ +void ilclient_cleanup_components(COMPONENT_T *list[]) +{ + int i; + OMX_ERRORTYPE error; + + i=0; + while (list[i]) + { + ilclient_return_events(list[i]); + if (list[i]->comp) + { + error = OMX_FreeHandle(list[i]->comp); + + vc_assert(error == OMX_ErrorNone); + } + i++; + } + + i=0; + while (list[i]) + { + vcos_event_flags_delete(&list[i]->event); + vcos_semaphore_delete(&list[i]->sema); + vcos_free(list[i]); + list[i] = NULL; + i++; + } +} + +/*********************************************************** + * Name: ilclient_change_component_state + * + * Description: changes the state of a single component. Note: this + * may not be suitable if the component is tunnelled and requires + * connected components to also change state. + * + * Returns: 0 on success, -1 on failure (note - trying to change to + * the same state which causes a OMX_ErrorSameState is treated as + * success) + ***********************************************************/ +int ilclient_change_component_state(COMPONENT_T *comp, OMX_STATETYPE state) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandStateSet, state, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandStateSet, state) < 0) + { + ilclient_debug_output("ilclient: could not change component state to %d", state); + ilclient_remove_event(comp, OMX_EventError, 0, 1, 0, 1); + return -1; + } + return 0; +} + +/*********************************************************** + * Name: ilclient_disable_port + * + * Description: disables a port on a given component. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_port(COMPONENT_T *comp, int portIndex) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandPortDisable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortDisable, portIndex) < 0) + vc_assert(0); +} + +/*********************************************************** + * Name: ilclient_enabled_port + * + * Description: enables a port on a given component. + * + * Returns: void + ***********************************************************/ +void ilclient_enable_port(COMPONENT_T *comp, int portIndex) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandPortEnable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortEnable, portIndex) < 0) + vc_assert(0); +} + + +/*********************************************************** + * Name: ilclient_enable_port_buffers + * + * Description: enables a port on a given component which requires + * buffers to be supplied by the client. + * + * Returns: void + ***********************************************************/ +int ilclient_enable_port_buffers(COMPONENT_T *comp, int portIndex, + ILCLIENT_MALLOC_T ilclient_malloc, + ILCLIENT_FREE_T ilclient_free, + void *private) +{ + OMX_ERRORTYPE error; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + OMX_BUFFERHEADERTYPE *list = NULL, **end = &list; + OMX_STATETYPE state; + int i; + + memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = portIndex; + + // work out buffer requirements, check port is in the right state + error = OMX_GetParameter(comp->comp, OMX_IndexParamPortDefinition, &portdef); + if(error != OMX_ErrorNone || portdef.bEnabled != OMX_FALSE || portdef.nBufferCountActual == 0 || portdef.nBufferSize == 0) + return -1; + + // check component is in the right state to accept buffers + error = OMX_GetState(comp->comp, &state); + if (error != OMX_ErrorNone || !(state == OMX_StateIdle || state == OMX_StateExecuting || state == OMX_StatePause)) + return -1; + + // send the command + error = OMX_SendCommand(comp->comp, OMX_CommandPortEnable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + + for (i=0; i != portdef.nBufferCountActual; i++) + { + unsigned char *buf; + if(ilclient_malloc) + buf = ilclient_malloc(private, portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname); + else + buf = vcos_malloc_aligned(portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname); + + if(!buf) + break; + + error = OMX_UseBuffer(comp->comp, end, portIndex, NULL, portdef.nBufferSize, buf); + if(error != OMX_ErrorNone) + { + if(ilclient_free) + ilclient_free(private, buf); + else + vcos_free(buf); + + break; + } + end = (OMX_BUFFERHEADERTYPE **) &((*end)->pAppPrivate); + } + + // queue these buffers + vcos_semaphore_wait(&comp->sema); + + if(portdef.eDir == OMX_DirInput) + { + *end = comp->in_list; + comp->in_list = list; + } + else + { + *end = comp->out_list; + comp->out_list = list; + } + + vcos_semaphore_post(&comp->sema); + + if(i != portdef.nBufferCountActual || + ilclient_wait_for_command_complete(comp, OMX_CommandPortEnable, portIndex) < 0) + { + ilclient_disable_port_buffers(comp, portIndex, NULL, ilclient_free, private); + + // at this point the first command might have terminated with an error, which means that + // the port is disabled before the disable_port_buffers function is called, so we're left + // with the error bit set and an error event in the queue. Clear these now if they exist. + ilclient_remove_event(comp, OMX_EventError, 0, 1, 1, 0); + + return -1; + } + + // success + return 0; +} + + +/*********************************************************** + * Name: ilclient_disable_port_buffers + * + * Description: disables a port on a given component which has + * buffers supplied by the client. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_port_buffers(COMPONENT_T *comp, int portIndex, + OMX_BUFFERHEADERTYPE *bufferList, + ILCLIENT_FREE_T ilclient_free, + void *private) +{ + OMX_ERRORTYPE error; + OMX_BUFFERHEADERTYPE *list = bufferList; + OMX_BUFFERHEADERTYPE **head, *clist, *prev; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + int num; + + // get the buffers off the relevant queue + memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = portIndex; + + // work out buffer requirements, check port is in the right state + error = OMX_GetParameter(comp->comp, OMX_IndexParamPortDefinition, &portdef); + if(error != OMX_ErrorNone || portdef.bEnabled != OMX_TRUE || portdef.nBufferCountActual == 0 || portdef.nBufferSize == 0) + return; + + num = portdef.nBufferCountActual; + + error = OMX_SendCommand(comp->comp, OMX_CommandPortDisable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + + while(num > 0) + { + VCOS_UNSIGNED set; + + if(list == NULL) + { + vcos_semaphore_wait(&comp->sema); + + // take buffers for this port off the relevant queue + head = portdef.eDir == OMX_DirInput ? &comp->in_list : &comp->out_list; + clist = *head; + prev = NULL; + + while(clist) + { + if((portdef.eDir == OMX_DirInput ? clist->nInputPortIndex : clist->nOutputPortIndex) == portIndex) + { + OMX_BUFFERHEADERTYPE *pBuffer = clist; + + if(!prev) + clist = *head = (OMX_BUFFERHEADERTYPE *) pBuffer->pAppPrivate; + else + clist = prev->pAppPrivate = (OMX_BUFFERHEADERTYPE *) pBuffer->pAppPrivate; + + pBuffer->pAppPrivate = list; + list = pBuffer; + } + else + { + prev = clist; + clist = (OMX_BUFFERHEADERTYPE *) &(clist->pAppPrivate); + } + } + + vcos_semaphore_post(&comp->sema); + } + + while(list) + { + void *buf = list->pBuffer; + OMX_BUFFERHEADERTYPE *next = list->pAppPrivate; + + error = OMX_FreeBuffer(comp->comp, portIndex, list); + vc_assert(error == OMX_ErrorNone); + + if(ilclient_free) + ilclient_free(private, buf); + else + vcos_free(buf); + + num--; + list = next; + } + + if(num) + { + OMX_U32 mask = ILCLIENT_PORT_DISABLED | ILCLIENT_EVENT_ERROR; + mask |= (portdef.eDir == OMX_DirInput ? ILCLIENT_EMPTY_BUFFER_DONE : ILCLIENT_FILL_BUFFER_DONE); + + // also wait for command complete/error in case we didn't have all the buffers allocated + vcos_event_flags_get(&comp->event, mask, VCOS_OR_CONSUME, -1, &set); + + if((set & ILCLIENT_EVENT_ERROR) && ilclient_remove_event(comp, OMX_EventError, 0, 1, 1, 0) >= 0) + return; + + if((set & ILCLIENT_PORT_DISABLED) && ilclient_remove_event(comp, OMX_EventCmdComplete, OMX_CommandPortDisable, 0, portIndex, 0) >= 0) + return; + } + } + + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortDisable, portIndex) < 0) + vc_assert(0); +} + + +/*********************************************************** + * Name: ilclient_setup_tunnel + * + * Description: creates a tunnel between components that require that + * ports be inititially disabled, then enabled after tunnel setup. If + * timeout is non-zero, it will initially wait until a port settings + * changes message has been received by the output port. If port + * streams are supported by the output port, the requested port stream + * will be selected. + * + * Returns: 0 indicates success, negative indicates failure. + * -1: a timeout waiting for the parameter changed + * -2: an error was returned instead of parameter changed + * -3: no streams are available from this port + * -4: requested stream is not available from this port + * -5: the data format was not acceptable to the sink + ***********************************************************/ +int ilclient_setup_tunnel(TUNNEL_T *tunnel, unsigned int portStream, int timeout) +{ + OMX_ERRORTYPE error; + OMX_PARAM_U32TYPE param; + OMX_STATETYPE state; + int32_t status; + int enable_error; + + // source component must at least be idle, not loaded + error = OMX_GetState(tunnel->source->comp, &state); + vc_assert(error == OMX_ErrorNone); + if (state == OMX_StateLoaded && ilclient_change_component_state(tunnel->source, OMX_StateIdle) < 0) + return -2; + + // wait for the port parameter changed from the source port + if(timeout) + { + status = ilclient_wait_for_event(tunnel->source, OMX_EventPortSettingsChanged, + tunnel->source_port, 0, -1, 1, + ILCLIENT_PARAMETER_CHANGED | ILCLIENT_EVENT_ERROR, timeout); + + if (status < 0) + { + ilclient_debug_output( + "ilclient: timed out waiting for port settings changed on port %d", tunnel->source_port); + return status; + } + } + + // disable ports + ilclient_disable_tunnel(tunnel); + + // if this source port uses port streams, we need to select one of them before proceeding + // if getparameter causes an error that's fine, nothing needs selecting + param.nSize = sizeof(OMX_PARAM_U32TYPE); + param.nVersion.nVersion = OMX_VERSION; + param.nPortIndex = tunnel->source_port; + if (OMX_GetParameter(tunnel->source->comp, OMX_IndexParamNumAvailableStreams, ¶m) == OMX_ErrorNone) + { + if (param.nU32 == 0) + { + // no streams available + // leave the source port disabled, and return a failure + return -3; + } + if (param.nU32 <= portStream) + { + // requested stream not available + // no streams available + // leave the source port disabled, and return a failure + return -4; + } + + param.nU32 = portStream; + error = OMX_SetParameter(tunnel->source->comp, OMX_IndexParamActiveStream, ¶m); + vc_assert(error == OMX_ErrorNone); + } + + // now create the tunnel + error = OMX_SetupTunnel(tunnel->source->comp, tunnel->source_port, tunnel->sink->comp, tunnel->sink_port); + + enable_error = 0; + + if (error != OMX_ErrorNone || (enable_error=ilclient_enable_tunnel(tunnel)) < 0) + { + // probably format not compatible + error = OMX_SetupTunnel(tunnel->source->comp, tunnel->source_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + error = OMX_SetupTunnel(tunnel->sink->comp, tunnel->sink_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + + if(enable_error) + { + //Clean up the errors. This does risk removing an error that was nothing to do with this tunnel :-/ + ilclient_remove_event(tunnel->sink, OMX_EventError, 0, 1, 0, 1); + ilclient_remove_event(tunnel->source, OMX_EventError, 0, 1, 0, 1); + } + + ilclient_debug_output("ilclient: could not setup/enable tunnel (setup=0x%x,enable=%d)", + error, enable_error); + return -5; + } + + return 0; +} + +/*********************************************************** + * Name: ilclient_wait_for_event + * + * Description: waits for a given event to appear on a component event + * list. If not immediately present, will wait on that components + * event group for the given event flag. + * + * Returns: 0 indicates success, negative indicates failure. + * -1: a timeout was received. + * -2: an error event was received. + * -3: a config change event was received. + ***********************************************************/ +int ilclient_wait_for_event(COMPONENT_T *comp, OMX_EVENTTYPE event, + OMX_U32 nData1, int ignore1, OMX_IN OMX_U32 nData2, int ignore2, + int event_flag, int suspend) +{ + int32_t status; + uint32_t set; + + while (ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) < 0) + { + // if we want to be notified of errors, check the list for an error now + // before blocking, the event flag may have been cleared already. + if(event_flag & ILCLIENT_EVENT_ERROR) + { + ILEVENT_T *cur; + ilclient_lock_events(comp->client); + cur = comp->list; + while(cur && cur->eEvent != OMX_EventError) + cur = cur->next; + + if(cur) + { + // clear error flag + vcos_event_flags_get(&comp->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + ilclient_unlock_events(comp->client); + return -2; + } + + ilclient_unlock_events(comp->client); + } + // check for config change event if we are asked to be notified of that + if(event_flag & ILCLIENT_CONFIG_CHANGED) + { + ILEVENT_T *cur; + ilclient_lock_events(comp->client); + cur = comp->list; + while(cur && cur->eEvent != OMX_EventParamOrConfigChanged) + cur = cur->next; + + ilclient_unlock_events(comp->client); + + if(cur) + return ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) == 0 ? 0 : -3; + } + + status = vcos_event_flags_get(&comp->event, event_flag, VCOS_OR_CONSUME, + suspend, &set); + if (status != 0) + return -1; + if (set & ILCLIENT_EVENT_ERROR) + return -2; + if (set & ILCLIENT_CONFIG_CHANGED) + return ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) == 0 ? 0 : -3; + } + + return 0; +} + + + +/*********************************************************** + * Name: ilclient_wait_for_command_complete_dual + * + * Description: Waits for an event signalling command completion. In + * this version we may also return failure if there is an error event + * that has terminated a command on a second component. + * + * Returns: 0 on success, -1 on failure of comp, -2 on failure of other + ***********************************************************/ +int ilclient_wait_for_command_complete_dual(COMPONENT_T *comp, OMX_COMMANDTYPE command, OMX_U32 nData2, COMPONENT_T *other) +{ + OMX_U32 mask = ILCLIENT_EVENT_ERROR; + int ret = 0; + + switch(command) { + case OMX_CommandStateSet: mask |= ILCLIENT_STATE_CHANGED; break; + case OMX_CommandPortDisable: mask |= ILCLIENT_PORT_DISABLED; break; + case OMX_CommandPortEnable: mask |= ILCLIENT_PORT_ENABLED; break; + default: return -1; + } + + if(other) + other->related = comp; + + while(1) + { + ILEVENT_T *cur, *prev = NULL; + VCOS_UNSIGNED set; + + ilclient_lock_events(comp->client); + + cur = comp->list; + while(cur && + !(cur->eEvent == OMX_EventCmdComplete && cur->nData1 == command && cur->nData2 == nData2) && + !(cur->eEvent == OMX_EventError && cur->nData2 == 1)) + { + prev = cur; + cur = cur->next; + } + + if(cur) + { + if(prev == NULL) + comp->list = cur->next; + else + prev->next = cur->next; + + // work out whether this was a success or a fail event + ret = cur->eEvent == OMX_EventCmdComplete || cur->nData1 == OMX_ErrorSameState ? 0 : -1; + + if(cur->eEvent == OMX_EventError) + vcos_event_flags_get(&comp->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + + // add back into spare list + cur->next = comp->client->event_list; + comp->client->event_list = cur; + cur->eEvent = -1; // mark as unused + + ilclient_unlock_events(comp->client); + break; + } + else if(other != NULL) + { + // check the other component for an error event that terminates a command + cur = other->list; + while(cur && !(cur->eEvent == OMX_EventError && cur->nData2 == 1)) + cur = cur->next; + + if(cur) + { + // we don't remove the event in this case, since the user + // can confirm that this event errored by calling wait_for_command on the + // other component + + ret = -2; + ilclient_unlock_events(comp->client); + break; + } + } + + ilclient_unlock_events(comp->client); + + vcos_event_flags_get(&comp->event, mask, VCOS_OR_CONSUME, VCOS_SUSPEND, &set); + } + + if(other) + other->related = NULL; + + return ret; +} + + +/*********************************************************** + * Name: ilclient_wait_for_command_complete + * + * Description: Waits for an event signalling command completion. + * + * Returns: 0 on success, -1 on failure. + ***********************************************************/ +int ilclient_wait_for_command_complete(COMPONENT_T *comp, OMX_COMMANDTYPE command, OMX_U32 nData2) +{ + return ilclient_wait_for_command_complete_dual(comp, command, nData2, NULL); +} + +/*********************************************************** + * Name: ilclient_get_output_buffer + * + * Description: Returns an output buffer returned from a component + * using the OMX_FillBufferDone callback from the output list for the + * given component and port index. + * + * Returns: pointer to buffer if available, otherwise NULL + ***********************************************************/ +OMX_BUFFERHEADERTYPE *ilclient_get_output_buffer(COMPONENT_T *comp, int portIndex, int block) +{ + OMX_BUFFERHEADERTYPE *ret = NULL, *prev = NULL; + VCOS_UNSIGNED set; + + do { + vcos_semaphore_wait(&comp->sema); + ret = comp->out_list; + while(ret != NULL && ret->nOutputPortIndex != portIndex) + { + prev = ret; + ret = ret->pAppPrivate; + } + + if(ret) + { + if(prev == NULL) + comp->out_list = ret->pAppPrivate; + else + prev->pAppPrivate = ret->pAppPrivate; + + ret->pAppPrivate = NULL; + } + vcos_semaphore_post(&comp->sema); + + if(block && !ret) + vcos_event_flags_get(&comp->event, ILCLIENT_FILL_BUFFER_DONE, VCOS_OR_CONSUME, -1, &set); + + } while(block && !ret); + + return ret; +} + +/*********************************************************** + * Name: ilclient_get_input_buffer + * + * Description: Returns an input buffer return from a component using + * the OMX_EmptyBufferDone callback from the output list for the given + * component and port index. + * + * Returns: pointer to buffer if available, otherwise NULL + ***********************************************************/ +OMX_BUFFERHEADERTYPE *ilclient_get_input_buffer(COMPONENT_T *comp, int portIndex, int block) +{ + OMX_BUFFERHEADERTYPE *ret = NULL, *prev = NULL; + + do { + VCOS_UNSIGNED set; + + vcos_semaphore_wait(&comp->sema); + ret = comp->in_list; + while(ret != NULL && ret->nInputPortIndex != portIndex) + { + prev = ret; + ret = ret->pAppPrivate; + } + + if(ret) + { + if(prev == NULL) + comp->in_list = ret->pAppPrivate; + else + prev->pAppPrivate = ret->pAppPrivate; + + ret->pAppPrivate = NULL; + } + vcos_semaphore_post(&comp->sema); + + if(block && !ret) + vcos_event_flags_get(&comp->event, ILCLIENT_EMPTY_BUFFER_DONE, VCOS_OR_CONSUME, -1, &set); + + } while(block && !ret); + + return ret; +} + +/*********************************************************** + * Name: ilclient_debug_output + * + * Description: prints a varg message to the log or the debug screen + * under win32 + * + * Returns: void + ***********************************************************/ +void ilclient_debug_output(char *format, ...) +{ + va_list args; + + va_start(args, format); + vcos_vlog_info(format, args); + va_end(args); +} + +/****************************************************************************** +Static functions +******************************************************************************/ + +/*********************************************************** + * Name: ilclient_lock_events + * + * Description: locks the client event structure + * + * Returns: void + ***********************************************************/ +static void ilclient_lock_events(ILCLIENT_T *st) +{ + vcos_semaphore_wait(&st->event_sema); +} + +/*********************************************************** + * Name: ilclient_unlock_events + * + * Description: unlocks the client event structure + * + * Returns: void + ***********************************************************/ +static void ilclient_unlock_events(ILCLIENT_T *st) +{ + vcos_semaphore_post(&st->event_sema); +} + +/*********************************************************** + * Name: ilclient_event_handler + * + * Description: event handler passed to core to use as component + * callback + * + * Returns: success + ***********************************************************/ +static OMX_ERRORTYPE ilclient_event_handler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, + OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + ILEVENT_T *event; + OMX_ERRORTYPE error = OMX_ErrorNone; + + ilclient_lock_events(st->client); + + // go through the events on this component and remove any duplicates in the + // existing list, since the client probably doesn't need them. it's better + // than asserting when we run out. + event = st->list; + while(event != NULL) + { + ILEVENT_T **list = &(event->next); + while(*list != NULL) + { + if((*list)->eEvent == event->eEvent && + (*list)->nData1 == event->nData1 && + (*list)->nData2 == event->nData2) + { + // remove this duplicate + ILEVENT_T *rem = *list; + ilclient_debug_output("%s: removing %d/%d/%d", st->name, event->eEvent, event->nData1, event->nData2); + *list = rem->next; + rem->eEvent = -1; + rem->next = st->client->event_list; + st->client->event_list = rem; + } + else + list = &((*list)->next); + } + + event = event->next; + } + + vc_assert(st->client->event_list); + event = st->client->event_list; + + switch (eEvent) { + case OMX_EventCmdComplete: + switch (nData1) { + case OMX_CommandStateSet: + ilclient_debug_output("%s: callback state changed (%s)", st->name, states[nData2]); + vcos_event_flags_set(&st->event, ILCLIENT_STATE_CHANGED, VCOS_OR); + break; + case OMX_CommandPortDisable: + ilclient_debug_output("%s: callback port disable %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_DISABLED, VCOS_OR); + break; + case OMX_CommandPortEnable: + ilclient_debug_output("%s: callback port enable %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_ENABLED, VCOS_OR); + break; + case OMX_CommandFlush: + ilclient_debug_output("%s: callback port flush %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_FLUSH, VCOS_OR); + break; + case OMX_CommandMarkBuffer: + ilclient_debug_output("%s: callback mark buffer %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_MARKED_BUFFER, VCOS_OR); + break; + default: + vc_assert(0); + } + break; + case OMX_EventError: + { + // check if this component failed a command, and we have to notify another command + // of this failure + if(nData2 == 1 && st->related != NULL) + vcos_event_flags_set(&st->related->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + + error = nData1; + switch (error) { + case OMX_ErrorPortUnpopulated: + if (st->error_mask & ILCLIENT_ERROR_UNPOPULATED) + { + ilclient_debug_output("%s: ignore error: port unpopulated (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: port unpopulated %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorSameState: + if (st->error_mask & ILCLIENT_ERROR_SAMESTATE) + { + ilclient_debug_output("%s: ignore error: same state (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: same state %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorBadParameter: + if (st->error_mask & ILCLIENT_ERROR_BADPARAMETER) + { + ilclient_debug_output("%s: ignore error: bad parameter (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: bad parameter %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorIncorrectStateTransition: + ilclient_debug_output("%s: incorrect state transition %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorBadPortIndex: + ilclient_debug_output("%s: bad port index %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorStreamCorrupt: + ilclient_debug_output("%s: stream corrupt %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorInsufficientResources: + ilclient_debug_output("%s: insufficient resources %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorUnsupportedSetting: + ilclient_debug_output("%s: unsupported setting %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorOverflow: + ilclient_debug_output("%s: overflow %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDiskFull: + ilclient_debug_output("%s: disk full %x (%d)", st->name, error, nData2); + //we do not set the error + break; + case OMX_ErrorMaxFileSize: + ilclient_debug_output("%s: max file size %x (%d)", st->name, error, nData2); + //we do not set the error + break; + case OMX_ErrorDrmUnauthorised: + ilclient_debug_output("%s: drm file is unauthorised %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDrmExpired: + ilclient_debug_output("%s: drm file has expired %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDrmGeneral: + ilclient_debug_output("%s: drm library error %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + default: + vc_assert(0); + ilclient_debug_output("%s: unexpected error %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + } + } + break; + case OMX_EventBufferFlag: + ilclient_debug_output("%s: buffer flag %d/%x", st->name, nData1, nData2); + if (nData2 & OMX_BUFFERFLAG_EOS) + { + vcos_event_flags_set(&st->event, ILCLIENT_BUFFER_FLAG_EOS, VCOS_OR); + nData2 = OMX_BUFFERFLAG_EOS; + } + else + vc_assert(0); + break; + case OMX_EventPortSettingsChanged: + ilclient_debug_output("%s: port settings changed %d", st->name, nData1); + vcos_event_flags_set(&st->event, ILCLIENT_PARAMETER_CHANGED, VCOS_OR); + break; + case OMX_EventMark: + ilclient_debug_output("%s: buffer mark %p", st->name, pEventData); + vcos_event_flags_set(&st->event, ILCLIENT_BUFFER_MARK, VCOS_OR); + break; + case OMX_EventParamOrConfigChanged: + ilclient_debug_output("%s: param/config 0x%X on port %d changed", st->name, nData2, nData1); + vcos_event_flags_set(&st->event, ILCLIENT_CONFIG_CHANGED, VCOS_OR); + break; + default: + vc_assert(0); + break; + } + + if (event) + { + // fill in details + event->eEvent = eEvent; + event->nData1 = nData1; + event->nData2 = nData2; + event->pEventData = pEventData; + + // remove from top of spare list + st->client->event_list = st->client->event_list->next; + + // put at head of component event queue + event->next = st->list; + st->list = event; + } + ilclient_unlock_events(st->client); + + // now call any callbacks without the event lock so the client can + // remove the event in context + switch(eEvent) { + case OMX_EventError: + if(event && st->client->error_callback) + st->client->error_callback(st->client->error_callback_data, st, error); + break; + case OMX_EventBufferFlag: + if ((nData2 & OMX_BUFFERFLAG_EOS) && st->client->eos_callback) + st->client->eos_callback(st->client->eos_callback_data, st, nData1); + break; + case OMX_EventPortSettingsChanged: + if (st->client->port_settings_callback) + st->client->port_settings_callback(st->client->port_settings_callback_data, st, nData1); + break; + case OMX_EventParamOrConfigChanged: + if (st->client->configchanged_callback) + st->client->configchanged_callback(st->client->configchanged_callback_data, st, nData2); + break; + default: + // ignore + break; + } + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_empty_buffer_done + * + * Description: passed to core to use as component callback, puts + * buffer on list + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_empty_buffer_done(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + OMX_BUFFERHEADERTYPE *list; + + ilclient_debug_output("%s: empty buffer done %p", st->name, pBuffer); + + vcos_semaphore_wait(&st->sema); + // insert at end of the list, so we process buffers in + // the same order + list = st->in_list; + while(list && list->pAppPrivate) + list = list->pAppPrivate; + + if(!list) + st->in_list = pBuffer; + else + list->pAppPrivate = pBuffer; + + pBuffer->pAppPrivate = NULL; + vcos_semaphore_post(&st->sema); + + vcos_event_flags_set(&st->event, ILCLIENT_EMPTY_BUFFER_DONE, VCOS_OR); + + if (st->client->empty_buffer_done_callback) + st->client->empty_buffer_done_callback(st->client->empty_buffer_done_callback_data, st); + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_empty_buffer_done_error + * + * Description: passed to core to use as component callback, asserts + * on use as client not expecting component to use this callback. + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_empty_buffer_done_error(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + vc_assert(0); + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_fill_buffer_done + * + * Description: passed to core to use as component callback, puts + * buffer on list + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_fill_buffer_done(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + OMX_BUFFERHEADERTYPE *list; + + ilclient_debug_output("%s: fill buffer done %p", st->name, pBuffer); + + vcos_semaphore_wait(&st->sema); + // insert at end of the list, so we process buffers in + // the correct order + list = st->out_list; + while(list && list->pAppPrivate) + list = list->pAppPrivate; + + if(!list) + st->out_list = pBuffer; + else + list->pAppPrivate = pBuffer; + + pBuffer->pAppPrivate = NULL; + vcos_semaphore_post(&st->sema); + + vcos_event_flags_set(&st->event, ILCLIENT_FILL_BUFFER_DONE, VCOS_OR); + + if (st->client->fill_buffer_done_callback) + st->client->fill_buffer_done_callback(st->client->fill_buffer_done_callback_data, st); + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_fill_buffer_done_error + * + * Description: passed to core to use as component callback, asserts + * on use as client not expecting component to use this callback. + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_fill_buffer_done_error(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) +{ + vc_assert(0); + return OMX_ErrorNone; +} + + + +OMX_HANDLETYPE ilclient_get_handle(COMPONENT_T *comp) +{ + vcos_assert(comp); + return comp->comp; +} + + +static struct { + OMX_PORTDOMAINTYPE dom; + int param; +} port_types[] = { + { OMX_PortDomainVideo, OMX_IndexParamVideoInit }, + { OMX_PortDomainAudio, OMX_IndexParamAudioInit }, + { OMX_PortDomainImage, OMX_IndexParamImageInit }, + { OMX_PortDomainOther, OMX_IndexParamOtherInit }, +}; + +int ilclient_get_port_index(COMPONENT_T *comp, OMX_DIRTYPE dir, OMX_PORTDOMAINTYPE type, int index) +{ + uint32_t i; + // for each possible port type... + for (i=0; iILCLIENT_T structure encapsulates the state needed for the IL + * Client API. It contains a set of callback functions used to + * communicate with the user. It also includes a linked list of free + * event structures. + ***********************************************************/ +typedef struct _ILCLIENT_T ILCLIENT_T; + + +/** + * Each ILEVENT_T structure stores the result of an EventHandler + * callback from a component, storing the event message type and any + * parameters returned. + ***********************************************************/ +typedef struct _ILEVENT_T ILEVENT_T; + + + +struct _COMPONENT_T; + +/** + * The COMPONENT_T structure represents an IL component, + * together with the necessary extra information required by the IL + * Client API. This structure stores the handle to the OMX component, + * as well as the event list containing all events sent by this + * component. The component state structure also holds a pair of + * buffer queues, for input and output buffers returned to the client + * by the FillBufferDone and EmptyBufferDone + * callbacks. As some operations result in error callbacks that can + * be ignored, an error mask is maintained to allow some errors to be + * ignored. A pointer to the client state structure is also added. + ***********************************************************/ +typedef struct _COMPONENT_T COMPONENT_T; + + +/** + * The generic callback function is used for communicating events from + * a particular component to the user. + * + * @param userdata The data returned from when the callback was registered. + * + * @param comp The component structure representing the component that + * originated this event. + * + * @param data The relevant data field from the event. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_CALLBACK_T)(void *userdata, COMPONENT_T *comp, OMX_U32 data); + + +/** + * The buffer callback function is used for indicating that a + * component has returned a buffer on a port using client buffer + * communication. + * + * @param data The data returned from when the callback was registered. + * + * @param comp The component from which the buffer originated. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_BUFFER_CALLBACK_T)(void *data, COMPONENT_T *comp); + + +/** + * The malloc function is passed into + * ilclient_enable_port_buffers() and used for allocating the + * buffer payload. + * + * @param userdata Private pointer passed into + * ilclient_enable_port_buffers() call. + * + * @param size Size in bytes of the requested memory region. + * + * @param align Alignment requirement in bytes for the base memory address. + * + * @param description Text description of the memory being allocated. + * + * @return The memory address on success, NULL on failure. + ***********************************************************/ +typedef void *(*ILCLIENT_MALLOC_T)(void *userdata, VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *description); + + +/** + * The free function is passed into + * ilclient_enable_port_buffers() and + * ilclient_disable_port_buffers() and used for freeing the + * buffer payload. + * + * @param userdata Private pointer passed into + * ilclient_enable_port_buffers() and + * ilclient_disable_port_buffers(). + * + * @param pointer Memory address to free, that was previously returned + * from ILCLIENT_MALLOC_T function. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_FREE_T)(void *userdata, void *pointer); + + +/** + * The event mask enumeration describes the possible events that the + * user can ask to wait for when waiting for a particular event. + ***********************************************************/ +typedef enum { + ILCLIENT_EMPTY_BUFFER_DONE = 0x1, /**< Set when a buffer is + returned from an input + port */ + + ILCLIENT_FILL_BUFFER_DONE = 0x2, /**< Set when a buffer is + returned from an output + port */ + + ILCLIENT_PORT_DISABLED = 0x4, /**< Set when a port indicates + it has completed a disable + command. */ + + ILCLIENT_PORT_ENABLED = 0x8, /**< Set when a port indicates + is has completed an enable + command. */ + + ILCLIENT_STATE_CHANGED = 0x10, /**< Set when a component + indicates it has completed + a state change command. */ + + ILCLIENT_BUFFER_FLAG_EOS = 0x20, /**< Set when a port signals + an EOS event. */ + + ILCLIENT_PARAMETER_CHANGED = 0x40, /**< Set when a port signals a + port settings changed + event. */ + + ILCLIENT_EVENT_ERROR = 0x80, /**< Set when a component + indicates an error. */ + + ILCLIENT_PORT_FLUSH = 0x100, /**< Set when a port indicates + is has completed a flush + command. */ + + ILCLIENT_MARKED_BUFFER = 0x200, /**< Set when a port indicates + it has marked a buffer. */ + + ILCLIENT_BUFFER_MARK = 0x400, /**< Set when a port indicates + it has received a buffer + mark. */ + + ILCLIENT_CONFIG_CHANGED = 0x800 /**< Set when a config parameter + changed. */ +} ILEVENT_MASK_T; + + +/** + * On component creation the user can set flags to control the + * creation of that component. + ***********************************************************/ +typedef enum { + ILCLIENT_FLAGS_NONE = 0x0, /**< Used if no flags are + set. */ + + ILCLIENT_ENABLE_INPUT_BUFFERS = 0x1, /**< If set we allow the + client to communicate with + input ports via buffer + communication, rather than + tunneling with another + component. */ + + ILCLIENT_ENABLE_OUTPUT_BUFFERS = 0x2, /**< If set we allow the + client to communicate with + output ports via buffer + communication, rather than + tunneling with another + component. */ + + ILCLIENT_DISABLE_ALL_PORTS = 0x4, /**< If set we disable all + ports on creation. */ + + ILCLIENT_HOST_COMPONENT = 0x8, /**< Create a host component. + The default host ilcore + only can create host components + by being locally hosted + so should only be used for testing + purposes. */ + + ILCLIENT_OUTPUT_ZERO_BUFFERS = 0x10 /**< All output ports will have + nBufferCountActual set to zero, + if supported by the component. */ +} ILCLIENT_CREATE_FLAGS_T; + + +/** + * \brief This structure represents a tunnel in the OpenMAX IL API. + * + * Some operations in this API act on a tunnel, so the tunnel state + * structure (TUNNEL_T) is a convenient store of the source and sink + * of the tunnel. For each, a pointer to the relevant component state + * structure and the port index is stored. + ***********************************************************/ +typedef struct { + COMPONENT_T *source; /**< The source component */ + int source_port; /**< The output port index on the source component */ + COMPONENT_T *sink; /**< The sink component */ + int sink_port; /**< The input port index on the sink component */ +} TUNNEL_T; + + +/** + * The set_tunnel macro is a useful function that initialises a + * TUNNEL_T structure. + ***********************************************************/ +#define set_tunnel(t,a,b,c,d) do {TUNNEL_T *_ilct = (t); \ + _ilct->source = (a); _ilct->source_port = (b); \ + _ilct->sink = (c); _ilct->sink_port = (d);} while(0) + +/** + * For calling OpenMAX IL methods directory, we need to access the + * OMX_HANDLETYPE corresponding to the COMPONENT_T structure. This + * macro enables this while keeping the COMPONENT_T structure opaque. + * The parameter x should be of the type *COMPONENT_T. + ***********************************************************/ +#define ILC_GET_HANDLE(x) ilclient_get_handle(x) + +/** + * An IL Client structure is created by the ilclient_init() + * method. This structure is used when creating components, but + * otherwise is not needed in other API functions as a pointer to this + * structure is maintained in the COMPONENT_T structure. + * + * @return pointer to client structure + ***********************************************************/ +VCHPRE_ ILCLIENT_T VCHPOST_ *ilclient_init(void); + +/** + * When all components have been deleted, the IL Client structure can + * be destroyed by calling the ilclient_destroy() function. + * + * @param handle The client handle. After calling this function, this + * handle should not be used. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_destroy(ILCLIENT_T *handle); + +/** + * The ilclient_set_port_settings_callback() function registers a + * callback to be used when the OMX_EventPortSettingsChanged event is + * received. When the event is received, a pointer to the component + * structure and port index is returned by the callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_port_settings_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_eos_callback() function registers a callback to be + * used when the OMX_EventBufferFlag is received with the + * OMX_BUFFERFLAG_EOS flag set. When the event is received, a pointer + * to the component structure and port index is returned by the + * callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_eos_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_error_callback() function registers a callback to be + * used when the OMX_EventError is received from a component. When + * the event is received, a pointer to the component structure and the + * error code are reported by the callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_error_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_configchanged_callback() function + * registers a callback to be used when an + * OMX_EventParamOrConfigChanged event occurs. When the + * event is received, a pointer to the component structure and the + * index value that has changed are reported by the callback. The + * user may then use an OMX_GetConfig call with the index + * as specified to retrieve the updated information. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_configchanged_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + + +/** + * The ilclient_set_fill_buffer_done_callback() function registers a + * callback to be used when a buffer passed to an output port using the + * OMX_FillBuffer call is returned with the OMX_FillBufferDone + * callback. When the event is received, a pointer to the component + * structure is returned by the callback. The user may then use the + * ilclient_get_output_buffer() function to retrieve the buffer. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_fill_buffer_done_callback(ILCLIENT_T *handle, + ILCLIENT_BUFFER_CALLBACK_T func, + void *userdata); + +/** + * The ilclient_set_empty_buffer_done_callback() function registers a + * callback to be used when a buffer passed to an input port using the + * OMX_EmptyBuffer call is returned with the OMX_EmptyBufferDone + * callback. When the event is received, a pointer to the component + * structure is returned by the callback. The user may then use the + * ilclient_get_input_buffer() function to retrieve the buffer. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a NULL function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_empty_buffer_done_callback(ILCLIENT_T *handle, + ILCLIENT_BUFFER_CALLBACK_T func, + void *userdata); + + +/** + * Components are created using the ilclient_create_component() + * function. + * + * @param handle The client handle + * + * @param comp On successful creation, the component structure pointer + * will be written back into comp. + * + * @param name The name of the component to be created. Component + * names as provided are automatically prefixed with + * "OMX.broadcom." before passing to the IL core. The name + * provided will also be used in debugging messages added about this + * component. + * + * @param flags The client can specify some creation behaviour by using + * the flags field. The meaning of each flag is defined + * by the ILCLIENT_CREATE_FLAGS_T type. + * + * @return 0 on success, -1 on failure + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_create_component(ILCLIENT_T *handle, + COMPONENT_T **comp, + char *name, + ILCLIENT_CREATE_FLAGS_T flags); + +/** + * The ilclient_cleanup_components() function deallocates all + * state associated with components and frees the OpenMAX component + * handles. All tunnels connecting components should have been torn + * down explicitly, and all components must be in loaded state. + * + * @param list A null-terminated list of component pointers to be + * deallocated. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_cleanup_components(COMPONENT_T *list[]); + + +/** + * The ilclient_change_component_state() function changes the + * state of an individual component. This will trigger the state + * change, and also wait for that state change to be completed. It + * should not be called if this state change has dependencies on other + * components also changing states. Trying to change a component to + * its current state is treated as success. + * + * @param comp The component to change. + * + * @param state The new state to transition to. + * + * @return 0 on success, -1 on failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_change_component_state(COMPONENT_T *comp, + OMX_STATETYPE state); + + +/** + * The ilclient_state_transition() function transitions a set of + * components that need to perform a simultaneous state transition; + * for example, when two components are tunnelled and the buffer + * supplier port needs to allocate and pass buffers to a non-supplier + * port. All components are sent a command to change state, then the + * function will wait for all components to signal that they have + * changed state. + * + * @param list A null-terminated list of component pointers. + * + * @param state The new state to which to transition all components. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_state_transition(COMPONENT_T *list[], + OMX_STATETYPE state); + + +/** + * The ilclient_disable_port() function disables a port on a + * given component. This function sends the disable port message to + * the component and waits for the component to signal that this has + * taken place. If the port is already disabled, this is treated as a + * success. + * + * @param comp The component containing the port to disable. + * + * @param portIndex The port index of the port to disable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_disable_port(COMPONENT_T *comp, + int portIndex); + + +/** + * The ilclient_enable_port() function enables a port on a + * given component. This function sends the enable port message to + * the component and waits for the component to signal that this has + * taken place. If the port is already disabled, this is treated as a + * success. + * + * @param comp The component containing the port to enable. + * + * @param portIndex The port index of the port to enable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_enable_port(COMPONENT_T *comp, + int portIndex); + + + +/** + * The ilclient_enable_port_buffers() function enables a port + * in base profile mode on a given component. The port is not + * tunneled, so requires buffers to be allocated. + * + * @param comp The component containing the port to enable. + * + * @param portIndex The port index of the port to enable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @param ilclient_malloc This function will be used to allocate + * buffer payloads. If NULL then + * vcos_malloc_aligned will be used. + * + * @param ilclient_free If an error occurs, this function is used to + * free previously allocated payloads. If NULL then + * vcos_free will be used. + * + * @param userdata The first argument to calls to + * ilclient_malloc and ilclient_free, if these + * arguments are not NULL. + * + * @return 0 indicates success. -1 indicates failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_enable_port_buffers(COMPONENT_T *comp, + int portIndex, + ILCLIENT_MALLOC_T ilclient_malloc, + ILCLIENT_FREE_T ilclient_free, + void *userdata); + + +/** + * The ilclient_disable_port_buffers() function disables a + * port in base profile mode on a given component. The port is not + * tunneled, and has been supplied with buffers by the client. + * + * @param comp The component containing the port to disable. + * + * @param portIndex The port index of the port to disable. This must + * be a named port index, rather than a OMX_ALL value. + * + * @param bufferList A list of buffers, using pAppPrivate + * as the next pointer that were allocated on this port, and currently + * held by the application. If buffers on this port are held by IL + * client or the component then these are automatically freed. + * + * @param ilclient_free This function is used to free the buffer payloads. + * If NULL then vcos_free will be used. + * + * @param userdata The first argument to calls to + * ilclient_free. + * + * @return void + */ +VCHPRE_ void VCHPOST_ ilclient_disable_port_buffers(COMPONENT_T *comp, + int portIndex, + OMX_BUFFERHEADERTYPE *bufferList, + ILCLIENT_FREE_T ilclient_free, + void *userdata); + + +/** + * With a populated tunnel structure, the + * ilclient_setup_tunnel() function connects the tunnel. It + * first transitions the source component to idle if currently in + * loaded state, and then optionally checks the source event list for + * a port settings changed event from the source port. If this event + * is not in the event queue then this function optionally waits for + * it to arrive. To disable this check for the port settings changed + * event, set timeout to zero. + * + * Both ports are then disabled, and the source port is inspected for + * a port streams parameter. If this is supported, then the + * portStream argument is used to select which port stream + * to use. The two ports are then tunnelled using the + * OMX_SetupTunnel function. If this is successful, then + * both ports are enabled. Note that for disabling and enabling the + * tunnelled ports, the functions ilclient_disable_tunnel() + * and ilclient_enable_tunnel() are used, so the relevant + * documentation for those functions applies here. + * + * @param tunnel The tunnel structure representing the tunnel to + * set up. + * + * @param portStream If port streams are supported on the output port + * of the tunnel, then this parameter indicates the port stream to + * select on this port. + * + * @param timeout The time duration in milliseconds to wait for the + * output port to signal a port settings changed event before + * returning a timeout failure. If this is 0, then we do not check + * for a port settings changed before setting up the tunnel. + * + * @return 0 indicates success, negative indicates failure: + * - -1: a timeout waiting for the parameter changed + * - -2: an error was returned instead of parameter changed + * - -3: no streams are available from this port + * - -4: requested stream is not available from this port + * - -5: the data format was not acceptable to the sink + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_setup_tunnel(TUNNEL_T *tunnel, + unsigned int portStream, + int timeout); + + +/** + * The ilclient_disable_tunnel() function disables both ports listed in + * the tunnel structure. It will send a port disable command to each + * port, then waits for both to indicate they have completed the + * transition. The errors OMX_ErrorPortUnpopulated and + * OMX_ErrorSameState are both ignored by this function; the former + * since the first port to disable may deallocate buffers before the + * second port has been disabled, leading to the second port reporting + * the unpopulated error. + * + * @param tunnel The tunnel to disable. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_disable_tunnel(TUNNEL_T *tunnel); + + +/** + * The ilclient_enable_tunnel() function enables both ports listed in + * the tunnel structure. It will first send a port enable command to + * each port. It then checks whether the sink component is not in + * loaded state - if so, the function waits for both ports to complete + * the requested port enable. If the sink component was in loaded + * state, then this component is transitioned to idle to allow the + * ports to exchange buffers and enable the ports. This is since + * typically this function is used when creating a tunnel between two + * components, where the source component is processing data to enable + * it to report the port settings changed event, and the sink port has + * yet to be used. Before transitioning the sink component to idle, + * this function waits for the sink port to be enabled - since the + * component is in loaded state, this will happen quickly. If the + * transition to idle fails, the sink component is transitioned back + * to loaded and the source port disabled. If the transition + * succeeds, the function then waits for the source port to complete + * the requested port enable. + * + * @param tunnel The tunnel to enable. + * + * @return 0 on success, -1 on failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_enable_tunnel(TUNNEL_T *tunnel); + + +/** + * The ilclient_flush_tunnels() function will flush a number of tunnels + * from the list of tunnels presented. For each tunnel that is to be + * flushed, both source and sink ports are sent a flush command. The + * function then waits for both ports to report they have completed + * the flush operation. + * + * @param tunnel List of tunnels. The list must be terminated with a + * tunnel structure with NULL component entries. + * + * @param max The maximum number of tunnels to flush from the list. + * A value of 0 indicates that all tunnels in the list are flushed. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_flush_tunnels(TUNNEL_T *tunnel, + int max); + + +/** + * The ilclient_teardown_tunnels() function tears down all tunnels in + * the list of tunnels presented. For each tunnel in the list, the + * OMX_SetupTunnel is called on the source port and on the sink port, + * where for both calls the destination component is NULL and the + * destination port is zero. The VMCSX IL implementation requires + * that all tunnels are torn down in this manner before components are + * freed. + * + * @param tunnels List of tunnels to teardown. The list must be + * terminated with a tunnel structure with NULL component entries. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_teardown_tunnels(TUNNEL_T *tunnels); + + +/** + * The ilclient_get_output_buffer() function returns a buffer + * that was sent to an output port and that has been returned from a + * component using the OMX_FillBufferDone callback. + * + * @param comp The component that returned the buffer. + * + * @param portIndex The port index on the component that the buffer + * was returned from. + * + * @param block If non-zero, the function will block until a buffer is available. + * + * @return Pointer to buffer if available, otherwise NULL. + ***********************************************************/ +VCHPRE_ OMX_BUFFERHEADERTYPE* VCHPOST_ ilclient_get_output_buffer(COMPONENT_T *comp, + int portIndex, + int block); + + +/** + * The ilclient_get_input_buffer() function returns a buffer + * that was sent to an input port and that has been returned from a + * component using the OMX_EmptyBufferDone callback. + * + * @param comp The component that returned the buffer. + * + * @param portIndex The port index on the component from which the buffer + * was returned. + * + * @param block If non-zero, the function will block until a buffer is available. + * + * @return pointer to buffer if available, otherwise NULL + ***********************************************************/ +VCHPRE_ OMX_BUFFERHEADERTYPE* VCHPOST_ ilclient_get_input_buffer(COMPONENT_T *comp, + int portIndex, + int block); + + +/** + * The ilclient_remove_event() function queries the event list for the + * given component, matching against the given criteria. If a matching + * event is found, it is removed and added to the free event list. + * + * @param comp The component that returned the matching event. + * + * @param event The event type of the matching event. + * + * @param nData1 The nData1 field of the matching event. + * + * @param ignore1 Whether to ignore the nData1 field when finding a + * matching event. A value of 0 indicates that nData1 must match, a + * value of 1 indicates that nData1 does not have to match. + * + * @param nData2 The nData2 field of the matching event. + * + * @param ignore2 Whether to ignore the nData2 field when finding a + * matching event. A value of 0 indicates that nData2 must match, a + * value of 1 indicates that nData2 does not have to match. + * + * @return 0 if the event was removed. -1 if no matching event was + * found. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_remove_event(COMPONENT_T *comp, + OMX_EVENTTYPE event, + OMX_U32 nData1, + int ignore1, + OMX_U32 nData2, + int ignore2); + + +/** + * The ilclient_return_events() function removes all events + * from a component event list and adds them to the IL client free + * event list. This function is automatically called when components + * are freed. + * + * @param comp The component from which all events should be moved to + * the free list. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_return_events(COMPONENT_T *comp); + + +/** + * The ilclient_wait_for_event() function is similar to + * ilclient_remove_event(), but allows the caller to block until that + * event arrives. + * + * @param comp The component that returned the matching event. + * + * @param event The event type of the matching event. + * + * @param nData1 The nData1 field of the matching event. + * + * @param ignore1 Whether to ignore the nData1 field when finding a + * matching event. A value of 0 indicates that nData1 must match, a + * value of 1 indicates that nData1 does not have to match. + * + * @param nData2 The nData2 field of the matching event. + * + * @param ignore2 Whether to ignore the nData2 field when finding a + * matching event. A value of 0 indicates that nData2 must match, a + * value of 1 indicates that nData2 does not have to match. + * + * @param event_flag Specifies a bitfield of IL client events to wait + * for, given in ILEVENT_MASK_T. If any of these events + * are signalled by the component, the event list is then re-checked + * for a matching event. If the ILCLIENT_EVENT_ERROR bit + * is included, and an error is signalled by the component, then the + * function will return an error code. If the + * ILCLIENT_CONFIG_CHANGED bit is included, and this bit is + * signalled by the component, then the function will return an error + * code. + * + * @param timeout Specifies how long to block for in milliseconds + * before returning a failure. + * + * @return 0 indicates success, a matching event has been removed from + * the component's event queue. A negative return indicates failure, + * in this case no events have been removed from the component's event + * queue. + * - -1: a timeout was received. + * - -2: an error event was received. + * - -3: a config changed event was received. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_event(COMPONENT_T *comp, + OMX_EVENTTYPE event, + OMX_U32 nData1, + int ignore1, + OMX_U32 nData2, + int ignore2, + int event_flag, + int timeout); + + +/** + * The ilclient_wait_for_command_complete() function waits + * for a message from a component that indicates that the command + * has completed. This is either a command success message, or an + * error message that signals the completion of an event. + * + * @param comp The component currently processing a command. + * + * @param command The command being processed. This must be either + * OMX_CommandStateSet, OMX_CommandPortDisable + * or OMX_CommandPortEnable. + * + * @param nData2 The expected value of nData2 in the + * command complete message. + * + * @return 0 indicates success, either the command successfully completed + * or the OMX_ErrorSameState was returned. -1 indicates + * that the command terminated with a different error message. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_command_complete(COMPONENT_T *comp, + OMX_COMMANDTYPE command, + OMX_U32 nData2); + + +/** + * The ilclient_wait_for_command_complete_dual() function + * is similar to ilclient_wait_for_command_complete(). The + * difference is that while waiting for the component to complete the + * event or raise an error, we can also report if another reports an + * error that terminates a command. This is useful if the two + * components are tunneled, and we need to wait for one component to + * enable a port, or change state to OMX_StateIdle. If the + * other component is the buffer supplier and reports an error, then + * it will not allocate buffers, so our first component may stall. + * + * @param comp The component currently processing a command. + * + * @param command The command being processed. This must be either + * OMX_CommandStateSet, OMX_CommandPortDisable + * or OMX_CommandPortEnable. + * + * @param nData2 The expected value of nData2 in the + * command complete message. + * + * @param related Another component, where we will return if this + * component raises an error that terminates a command. + * + * @return 0 indicates success, either the command successfully + * completed or the OMX_ErrorSameState was returned. -1 + * indicates that the command terminated with a different error + * message. -2 indicates that the related component raised an error. + * In this case, the error is not cleared from the related + * component's event list. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_command_complete_dual(COMPONENT_T *comp, + OMX_COMMANDTYPE command, + OMX_U32 nData2, + COMPONENT_T *related); + + +/** + * The ilclient_debug_output() function adds a message to a + * host-specific debug display. For a local VideoCore host the message is + * added to the internal message log. For a Win32 host the message is + * printed to the debug display. This function should be customised + * when IL client is ported to another platform. + * + * @param format A message to add, together with the variable + * argument list similar to printf and other standard C functions. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_debug_output(char *format, ...); + +/** + * The ilclient_get_handle() function returns the + * underlying OMX component held by an IL component handle. This is + * needed when calling methods such as OMX_SetParameter on + * a component created using the IL client library. + * + * @param comp IL component handle + * + * @return The OMX_HANDLETYPE value for the component. + ***********************************************************/ +VCHPRE_ OMX_HANDLETYPE VCHPOST_ ilclient_get_handle(COMPONENT_T *comp); + + +#ifndef OMX_SKIP64BIT + +/** + * Macro to return OMX_TICKS from a signed 64 bit value. + * This is the version where OMX_TICKS is a signed 64 bit + * value, an alternative definition is used when OMX_TICKS + * is a structure. + */ +#define ilclient_ticks_from_s64(s) (s) + +/** + * Macro to return signed 64 bit value from OMX_TICKS. + * This is the version where OMX_TICKS is a signed 64 bit + * value, an alternative definition is used when OMX_TICKS + * is a structure. + */ +#define ilclient_ticks_to_s64(t) (t) + +#else + +/** + * Inline function to return OMX_TICKS from a signed 64 bit + * value. This is the version where OMX_TICKS is a + * structure, an alternative definition is used when + * OMX_TICKS is a signed 64 bit value. + */ +static inline OMX_TICKS ilclient_ticks_from_s64(int64_t s) { + OMX_TICKS ret; + ret.nLowPart = s; + ret.nHighPart = s>>32; + return ret; +} + +/** + * Inline function to return signed 64 bit value from + * OMX_TICKS. This is the version where + * OMX_TICKS is a structure, an alternative definition is + * used when OMX_TICKS is a signed 64 bit value. + */ +static inline int64_t ilclient_ticks_to_s64(OMX_TICKS t) { + uint64_t u = t.nLowPart | ((uint64_t)t.nHighPart << 32); + return u; +} + + +#endif /* OMX_SKIP64BIT */ + +/** + * The ilclient_get_port_index() function returns the n'th + * port index of the specified type and direction for the given + * component. + * + * @param comp The component of interest + * @param dir The direction + * @param type The type, or -1 for any type. + * @param index Which port (counting from 0). + * + * @return The port index, or -1 if not found. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_get_port_index(COMPONENT_T *comp, + OMX_DIRTYPE dir, + OMX_PORTDOMAINTYPE type, + int index); + + +/** + * The ilclient_suggest_bufsize() function gives a + * component a hint about the size of buffer it should use. This size + * is set on the component by setting the + * OMX_IndexParamBrcmOutputBufferSize index on the given + * component. + * + * @param comp IL component handle + * @param nBufSizeHint Size of buffer in bytes + * + * @return 0 indicates success, -1 indicates failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_suggest_bufsize(COMPONENT_T *comp, + OMX_U32 nBufSizeHint); + + +/** + * The ilclient_stack_size() function suggests a minimum + * stack size that tasks calling into with API will require. + * + * @return Suggested stack size in bytes. + ***********************************************************/ +VCHPRE_ unsigned int VCHPOST_ ilclient_stack_size(void); + +#endif /* ILCLIENT_H */ diff --git a/host_applications/linux/apps/hello_pi/libs/ilclient/ilcore.c b/host_applications/linux/apps/hello_pi/libs/ilclient/ilcore.c new file mode 100755 index 0000000..356733d --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/ilclient/ilcore.c @@ -0,0 +1,308 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file + * + * \brief Host core implementation. + */ + +#include +#include + +//includes +#include +#include +#include +#include + +#include "IL/OMX_Component.h" +#include "interface/vcos/vcos.h" + +#include "interface/vmcs_host/vcilcs.h" +#include "interface/vmcs_host/vchost.h" +#include "interface/vmcs_host/vcilcs_common.h" + +static int coreInit = 0; +static int nActiveHandles = 0; +static ILCS_SERVICE_T *ilcs_service = NULL; +static VCOS_MUTEX_T lock; +static VCOS_ONCE_T once = VCOS_ONCE_INIT; + +/* Atomic creation of lock protecting shared state */ +static void initOnce(void) +{ + VCOS_STATUS_T status; + status = vcos_mutex_create(&lock, VCOS_FUNCTION); + vcos_demand(status == VCOS_SUCCESS); +} + +/* OMX_Init */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void) +{ + VCOS_STATUS_T status; + OMX_ERRORTYPE err = OMX_ErrorNone; + + status = vcos_once(&once, initOnce); + vcos_demand(status == VCOS_SUCCESS); + + vcos_mutex_lock(&lock); + + if(coreInit == 0) + { + // we need to connect via an ILCS connection to VideoCore + VCHI_INSTANCE_T initialise_instance; + VCHI_CONNECTION_T *connection; + ILCS_CONFIG_T config; + + vc_host_get_vchi_state(&initialise_instance, &connection); + + vcilcs_config(&config); + + ilcs_service = ilcs_init((VCHIQ_INSTANCE_T) initialise_instance, (void **) &connection, &config, 0); + + if(ilcs_service == NULL) + { + err = OMX_ErrorHardware; + goto end; + } + + coreInit = 1; + } + else + coreInit++; + +end: + vcos_mutex_unlock(&lock); + return err; +} + +/* OMX_Deinit */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void) +{ + if(coreInit == 0) // || (coreInit == 1 && nActiveHandles > 0)) + return OMX_ErrorNotReady; + + vcos_mutex_lock(&lock); + + coreInit--; + + if(coreInit == 0) + { + // we need to teardown the ILCS connection to VideoCore + ilcs_deinit(ilcs_service); + ilcs_service = NULL; + } + + vcos_mutex_unlock(&lock); + + return OMX_ErrorNone; +} + + +/* OMX_ComponentNameEnum */ +OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum( + OMX_OUT OMX_STRING cComponentName, + OMX_IN OMX_U32 nNameLength, + OMX_IN OMX_U32 nIndex) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_component_name_enum(ilcs_get_common(ilcs_service), cComponentName, nNameLength, nIndex); +} + + +/* OMX_GetHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle( + OMX_OUT OMX_HANDLETYPE* pHandle, + OMX_IN OMX_STRING cComponentName, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_CALLBACKTYPE* pCallBacks) +{ + OMX_ERRORTYPE eError; + OMX_COMPONENTTYPE *pComp; + OMX_HANDLETYPE hHandle = 0; + + if (pHandle == NULL || cComponentName == NULL || pCallBacks == NULL || ilcs_service == NULL) + { + if(pHandle) + *pHandle = NULL; + return OMX_ErrorBadParameter; + } + + { + pComp = (OMX_COMPONENTTYPE *)malloc(sizeof(OMX_COMPONENTTYPE)); + if (!pComp) + { + vcos_assert(0); + return OMX_ErrorInsufficientResources; + } + memset(pComp, 0, sizeof(OMX_COMPONENTTYPE)); + hHandle = (OMX_HANDLETYPE)pComp; + pComp->nSize = sizeof(OMX_COMPONENTTYPE); + pComp->nVersion.nVersion = OMX_VERSION; + eError = vcil_out_create_component(ilcs_get_common(ilcs_service), hHandle, cComponentName); + + if (eError == OMX_ErrorNone) { + // Check that all function pointers have been filled in. + // All fields should be non-zero. + int i; + uint32_t *p = (uint32_t *) pComp; + for(i=0; i>2; i++) + if(*p++ == 0) + eError = OMX_ErrorInvalidComponent; + + if(eError != OMX_ErrorNone && pComp->ComponentDeInit) + pComp->ComponentDeInit(hHandle); + } + + if (eError == OMX_ErrorNone) { + eError = pComp->SetCallbacks(hHandle,pCallBacks,pAppData); + if (eError != OMX_ErrorNone) + pComp->ComponentDeInit(hHandle); + } + if (eError == OMX_ErrorNone) { + *pHandle = hHandle; + } + else { + *pHandle = NULL; + free(pComp); + } + } + + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + nActiveHandles++; + vcos_mutex_unlock(&lock); + } + + return eError; +} + +/* OMX_FreeHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle( + OMX_IN OMX_HANDLETYPE hComponent) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pComp; + + if (hComponent == NULL || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + pComp = (OMX_COMPONENTTYPE*)hComponent; + + if (ilcs_service == NULL) + return OMX_ErrorBadParameter; + + eError = (pComp->ComponentDeInit)(hComponent); + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + --nActiveHandles; + vcos_mutex_unlock(&lock); + free(pComp); + } + + vcos_assert(nActiveHandles >= 0); + + return eError; +} + +/* OMX_SetupTunnel */ +OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel( + OMX_IN OMX_HANDLETYPE hOutput, + OMX_IN OMX_U32 nPortOutput, + OMX_IN OMX_HANDLETYPE hInput, + OMX_IN OMX_U32 nPortInput) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pCompIn, *pCompOut; + OMX_TUNNELSETUPTYPE oTunnelSetup; + + if ((hOutput == NULL && hInput == NULL) || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + oTunnelSetup.nTunnelFlags = 0; + oTunnelSetup.eSupplier = OMX_BufferSupplyUnspecified; + + pCompOut = (OMX_COMPONENTTYPE*)hOutput; + + if (hOutput){ + eError = pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, hInput, nPortInput, &oTunnelSetup); + } + + if (eError == OMX_ErrorNone && hInput) { + pCompIn = (OMX_COMPONENTTYPE*)hInput; + eError = pCompIn->ComponentTunnelRequest(hInput, nPortInput, hOutput, nPortOutput, &oTunnelSetup); + + if (eError != OMX_ErrorNone && hOutput) { + /* cancel tunnel request on output port since input port failed */ + pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, NULL, 0, NULL); + } + } + return eError; +} + +/* OMX_GetComponentsOfRole */ +OMX_ERRORTYPE OMX_GetComponentsOfRole ( + OMX_IN OMX_STRING role, + OMX_INOUT OMX_U32 *pNumComps, + OMX_INOUT OMX_U8 **compNames) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumComps = 0; + return eError; +} + +/* OMX_GetRolesOfComponent */ +OMX_ERRORTYPE OMX_GetRolesOfComponent ( + OMX_IN OMX_STRING compName, + OMX_INOUT OMX_U32 *pNumRoles, + OMX_OUT OMX_U8 **roles) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumRoles = 0; + return eError; +} + +/* OMX_GetDebugInformation */ +OMX_ERRORTYPE OMX_GetDebugInformation ( + OMX_OUT OMX_STRING debugInfo, + OMX_INOUT OMX_S32 *pLen) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_get_debug_information(ilcs_get_common(ilcs_service), debugInfo, pLen); +} + + + +/* File EOF */ + diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/Makefile b/host_applications/linux/apps/hello_pi/libs/vgfont/Makefile new file mode 100755 index 0000000..1e2a22b --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/Makefile @@ -0,0 +1,7 @@ +OBJS=font.o vgft.o graphics.o +LIB=libvgfont.a + +INCLUDES+=-I$(SDKSTAGE)/usr/include/freetype2 -I$(SDKSTAGE)/usr/include -I$(SDKSTAGE)/usr/include/arm-linux-gnueabi + +include ../../Makefile.include + diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/font.c b/host_applications/linux/apps/hello_pi/libs/vgfont/font.c new file mode 100755 index 0000000..7da2a4e --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/font.c @@ -0,0 +1,355 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +/** @file font.c + * + * Fairly primitive font handling, just enough to emulate the old API. + * + * Hinting and Font Size + * + * The old API does not create fonts explicitly, it just renders them + * as needed. That works fine for unhinted fonts, but for hinted fonts we + * care about font size. + * + * Since we now *can* do hinted fonts, we should do. Regenerating the + * fonts each time becomes quite slow, so we maintain a cache of fonts. + * + * For the typical applications which use graphics_x this is fine, but + * won't work well if lots of fonts sizes are used. + * + * Unicode + * + * This API doesn't support unicode at all at present, nor UTF-8. + */ + +#include +#include +#include +#include + +#include "graphics_x_private.h" +#include "vgft.h" + +#define VMCS_INSTALL_PREFIX "" + +/** The one and only (default) font we support for now. + */ +static struct +{ + const char *file; + void *mem; + size_t len; +} default_font = { "Vera.ttf" }; + +/** An entry in our list of fonts + */ +typedef struct gx_font_cache_entry_t +{ + struct gx_font_cache_entry_t *next; + VGFT_FONT_T font; + uint32_t ptsize; /** size in points, 26.6 */ +} gx_font_cache_entry_t; + +static char fname[128]; +static int inited; +static gx_font_cache_entry_t *fonts; + +static VGFT_FONT_T *find_font(const char *text, uint32_t text_size); + +VCOS_STATUS_T gx_priv_font_init(const char *font_dir) +{ + VCOS_STATUS_T ret; + size_t len; + int rc; + if (vgft_init()) + { + ret = VCOS_ENOMEM; + goto fail_init; + } + + int fd = -1; + // search for the font + sprintf(fname, "%s/%s", font_dir, default_font.file); + fd = open(fname, O_RDONLY); + + if (fd < 0) + { + GX_ERROR("Could not open font file '%s'", default_font.file); + ret = VCOS_ENOENT; + goto fail_open; + } + + len = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + + default_font.mem = vcos_malloc(len, default_font.file); + if (!default_font.mem) + { + GX_ERROR("No memory for font %s", fname); + ret = VCOS_ENOMEM; + goto fail_mem; + } + + rc = read(fd, default_font.mem, len); + if (rc != len) + { + GX_ERROR("Could not read font %s", fname); + ret = VCOS_EINVAL; + goto fail_rd; + } + default_font.len = len; + close(fd); + + GX_TRACE("Opened font file '%s'", fname); + + inited = 1; + return VCOS_SUCCESS; + +fail_rd: + vcos_free(default_font.mem); +fail_mem: + if (fd >= 0) close(fd); +fail_open: + vgft_term(); +fail_init: + return ret; +} + +void gx_priv_font_term(void) +{ + gx_font_cache_flush(); + vgft_term(); + vcos_free(default_font.mem); +} + +/** Render text. + * + * FIXME: Not at all optimal - re-renders each time. + * FIXME: Not UTF-8 aware + * FIXME: better caching + */ +VCOS_STATUS_T gx_priv_render_text( GX_DISPLAY_T *disp, + GRAPHICS_RESOURCE_HANDLE res, + int32_t x, + int32_t y, + uint32_t width, + uint32_t height, + uint32_t fg_colour, + uint32_t bg_colour, + const char *text, + uint32_t text_length, + uint32_t text_size ) +{ + VGfloat vg_colour[4]; + VGFT_FONT_T *font; + VGPaint fg; + GX_CLIENT_STATE_T save; + VCOS_STATUS_T status = VCOS_SUCCESS; + int clip = 1; + + vcos_demand(inited); // has gx_font_init() been called? + + gx_priv_save(&save, res); + + if (width == GRAPHICS_RESOURCE_WIDTH && + height == GRAPHICS_RESOURCE_HEIGHT) + { + clip = 0; + } + + width = (width == GRAPHICS_RESOURCE_WIDTH) ? res->width : width; + height = (height == GRAPHICS_RESOURCE_HEIGHT) ? res->height : height; + font = find_font(text, text_size); + if (!font) + { + status = VCOS_ENOMEM; + goto finish; + } + + // setup the clipping rectangle + if (clip) + { + VGint coords[] = {x,y,width,height}; + vgSeti(VG_SCISSORING, VG_TRUE); + vgSetiv(VG_SCISSOR_RECTS, 4, coords); + } + + // setup the background colour if needed + if (bg_colour != GRAPHICS_TRANSPARENT_COLOUR) + { + int err; + VGfloat rendered_w, rendered_h; + VGfloat vg_bg_colour[4]; + + // setup the background colour... + gx_priv_colour_to_paint(bg_colour, vg_bg_colour); + vgSetfv(VG_CLEAR_COLOR, 4, vg_bg_colour); + + // fill in a rectangle... + vgft_get_text_extents(font, text, text_length, (VGfloat)x, (VGfloat)y, &rendered_w, &rendered_h); + + if ( ( 0 < (VGint)rendered_w ) && ( 0 < (VGint)rendered_h ) ) + { + // Have to compensate for the messed up y position of multiline text. + VGfloat offset = vgft_first_line_y_offset(font); + int32_t bottom = y + offset - rendered_h; + + vgClear(x, bottom, (VGint)rendered_w, (VGint)rendered_h); + err = vgGetError(); + if (err) + { + GX_LOG("Error %d clearing bg text %d %d %g %g", + err, x, y, rendered_w, rendered_h); + vcos_assert(0); + } // if + } // if + } // if + // setup the foreground colour + fg = vgCreatePaint(); + if (!fg) + { + status = VCOS_ENOMEM; + goto finish; + } + + // draw the foreground text + vgSetParameteri(fg, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + gx_priv_colour_to_paint(fg_colour, vg_colour); + vgSetParameterfv(fg, VG_PAINT_COLOR, 4, vg_colour); + vgSetPaint(fg, VG_FILL_PATH); + + vgft_font_draw(font, (VGfloat)x, (VGfloat)y, text, text_length, VG_FILL_PATH); + + vgDestroyPaint(fg); + + vcos_assert(vgGetError() == 0); + vgSeti(VG_SCISSORING, VG_FALSE); + +finish: + gx_priv_restore(&save); + + return status; +} + + +/** Find a font in our cache, or create a new entry in the cache. + * + * Very primitive at present. + */ +static VGFT_FONT_T *find_font(const char *text, uint32_t text_size) +{ + int ptsize, dpi_x = 0, dpi_y = 0; + VCOS_STATUS_T status; + gx_font_cache_entry_t *font; + + ptsize = text_size << 6; // freetype takes size in points, in 26.6 format. + + for (font = fonts; font; font = font->next) + { + if (font->ptsize == ptsize) + return &font->font; + } + + font = vcos_malloc(sizeof(*font), "font"); + if (!font) + return NULL; + + font->ptsize = ptsize; + + status = vgft_font_init(&font->font); + if (status != VCOS_SUCCESS) + { + vcos_free(font); + return NULL; + } + + // load the font + status = vgft_font_load_mem(&font->font, default_font.mem, default_font.len); + if (status != VCOS_SUCCESS) + { + GX_LOG("Could not load font from memory: %d", status); + vgft_font_term(&font->font); + vcos_free(font); + return NULL; + } + + status = vgft_font_convert_glyphs(&font->font, ptsize, dpi_x, dpi_y); + if (status != VCOS_SUCCESS) + { + GX_LOG("Could not convert font '%s' at size %d", fname, ptsize); + vgft_font_term(&font->font); + vcos_free(font); + return NULL; + } + + font->next = fonts; + fonts = font; + + return &font->font; +} + +void gx_font_cache_flush(void) +{ + while (fonts != NULL) + { + struct gx_font_cache_entry_t *next = fonts->next; + vgft_font_term(&fonts->font); + vcos_free(fonts); + fonts = next; + } +} + +int32_t graphics_resource_text_dimensions_ext(GRAPHICS_RESOURCE_HANDLE res, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height, + const uint32_t text_size ) +{ + GX_CLIENT_STATE_T save; + VGfloat w, h; + int ret = -1; + + gx_priv_save(&save, res); + + VGFT_FONT_T *font = find_font(text, text_size); + if (!font) + goto finish; + + + vgft_get_text_extents(font, text, text_length, 0.0, 0.0, &w, &h); + *width = w; + *height = h; + ret = 0; + +finish: + gx_priv_restore(&save); + return ret; +} + diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/graphics.c b/host_applications/linux/apps/hello_pi/libs/vgfont/graphics.c new file mode 100755 index 0000000..20e1d5a --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/graphics.c @@ -0,0 +1,1609 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Graphics library for VG + +#include +#include +#include +#include +#include "vgfont.h" +#include "graphics_x_private.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define ATEXT_FONT_SIZE 12 /*< Default font size (font size can be set with *_ext functions). */ + +/****************************************************************************** +Local data +******************************************************************************/ +static GX_DISPLAY_T display; /*< Our one and only EGL display. */ + +/** + * We create one eglContext for each of the possible graphics_x resource types + * that are supported. + ***********************************************************/ +static EGLContext gx_contexts[GRAPHICS_RESOURCE_HANDLE_TYPE_MAX]; + +/** Note: we have to share all our contexts, because otherwise it seems + * to be not valid to blit from one image to another if the images + * have different contexts. + * + * That means we have to use a single global lock to serialise all accesses + * to any contexts. + ***********************************************************/ +static VCOS_MUTEX_T lock; + +static EGLConfig gx_configs[GRAPHICS_RESOURCE_HANDLE_TYPE_MAX]; + +static int inited; + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/** Convert graphics_x colour formats into EGL format. */ +static int gx_egl_attrib_colours(EGLint *attribs, GRAPHICS_RESOURCE_TYPE_T res_type) +{ + int i, n; + static EGLint rgba[] = {EGL_RED_SIZE, EGL_GREEN_SIZE, EGL_BLUE_SIZE, EGL_ALPHA_SIZE}; + static uint8_t rgb565[] = {5,6,5,0}; + static uint8_t rgb888[] = {8,8,8,0}; + static uint8_t rgb32a[] = {8,8,8,8}; + + uint8_t *sizes = NULL; + + switch (res_type) + { + case GRAPHICS_RESOURCE_RGB565: + sizes = rgb565; + break; + case GRAPHICS_RESOURCE_RGB888: + sizes = rgb888; + break; + case GRAPHICS_RESOURCE_RGBA32: + sizes = rgb32a; + break; + default: + vcos_assert(0); + return -1; + } + for (n=0, i=0; imagic == RES_MAGIC)); + vcos_assert(res == NULL || !res->context_bound); + + state->context = eglGetCurrentContext(); + state->api = eglQueryAPI(); + state->read_surface = eglGetCurrentSurface(EGL_READ); + state->draw_surface = eglGetCurrentSurface(EGL_DRAW); + state->res = res; + + vcos_assert(state->api); // should never be anything other than VG or GL + + vcos_mutex_lock(&lock); + + egl_result = eglBindAPI(EGL_OPENVG_API); + vcos_assert(egl_result); + + if (res) + { + GX_TRACE("gx_priv_save: eglMakeCurrent: %s, res %x surface %x, cxt %x", vcos_thread_get_name(vcos_thread_current()), + (uint32_t)res, (uint32_t)res->surface, (uint32_t)res->context); + + egl_result = eglMakeCurrent(display.disp, res->surface, + res->surface, res->context); + vcos_assert(egl_result); + + res->context_bound = 1; + } +} + +/*****************************************************************************/ +void gx_priv_restore(GX_CLIENT_STATE_T *state) +{ + EGLBoolean egl_result; + + GX_TRACE("gx_priv_restore: eglMakeCurrent: %s, res %x draw_surface %x, surface %x, cxt %x", vcos_thread_get_name(vcos_thread_current()), + (uint32_t)state->res, (uint32_t)state->draw_surface, (uint32_t)state->read_surface, (uint32_t)state->context); + + // disconnect our thread from this context, so we other threads can use it via + // this API + egl_result = eglMakeCurrent(display.disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + vcos_assert(egl_result); + + // now return to the client's API binding + egl_result = eglBindAPI(state->api); + vcos_assert(egl_result); + + egl_result = eglMakeCurrent(display.disp, state->draw_surface, state->read_surface, state->context); + vcos_assert(egl_result); + + if (state->res) state->res->context_bound = 0; + + vcos_mutex_unlock(&lock); +} + +/****************************************************************************** +Functions and data exported as part of the public GraphicsX API +******************************************************************************/ + +VCOS_LOG_CAT_T gx_log_cat; /*< Logging category for GraphicsX. */ + +int32_t graphics_initialise( void ) +{ + // dummy initialisation function. This is typically called + // early in the day before VLLs are available, and so cannot + // do anything useful. + return 0; +} + +/*****************************************************************************/ +int32_t graphics_uninitialise( void ) +{ + int i; + vcos_assert(inited); + + gx_priv_font_term(); + + for (i=0; iu.native_window, + &cookie); + if (rc < 0) + { + GX_LOG("%s: could not create native window", __FUNCTION__); + status = VCOS_ENOMEM; + goto fail_create_native_win; + } + + h->magic = RES_MAGIC; + h->type = GX_WINDOW; + h->alpha = 1.0; + + h->surface = eglCreateWindowSurface(display.disp, gx_configs[image_type], &h->u.native_window.egl_win, NULL); + if (!h->surface) + { + GX_LOG("Could not create window surface: 0x%x", eglGetError()); + status = VCOS_ENOMEM; + goto fail_win; + } + + egl_result = eglSurfaceAttrib(display.disp, h->surface, + EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); + vcos_assert(egl_result); + + h->context = gx_contexts[image_type]; + h->screen_id = screen_id; + h->width = width; + h->height = height; + h->restype = image_type; + + gx_priv_save(&save, h); + + // fill it with black + status = gx_priv_resource_fill(h, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0xff)); + vcos_assert(status == VCOS_SUCCESS); + + gx_priv_finish_native_window(h, cookie); + gx_priv_flush(h); + + *resource_handle = h; + gx_priv_restore(&save); + return status; + +fail_win: + gx_priv_destroy_native_window(h); +fail_create_native_win: + vcos_free(h); + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +int32_t graphics_delete_resource( GRAPHICS_RESOURCE_HANDLE res ) +{ + EGLBoolean result; + + if (!res) + { + // let it slide - mimics old behaviour + return 0; + } + GX_TRACE("delete resource @%p", res); + + vcos_assert(res->magic == RES_MAGIC); + + if (res->type == GX_PBUFFER) + { + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + vgDestroyImage(res->u.pixmap); + vcos_assert(vgGetError() == 0); + gx_priv_restore(&save); + } + + GX_TRACE("graphics_delete_resource: calling eglDestroySurface..."); + result = eglDestroySurface(display.disp, res->surface); + vcos_assert(result); + + GX_TRACE("graphics_delete_resource: calling eglWaitClient..."); + eglWaitClient(); // wait for EGL to finish sorting out its surfaces + + if (res->type == GX_WINDOW) + { + GX_TRACE("graphics_delete_resource: calling gx_priv_destroy_native_window..."); + gx_priv_destroy_native_window(res); + } + + res->magic = ~RES_MAGIC; + vcos_free(res); + GX_TRACE("graphics_delete_resource: done"); + + return 0; +} + +/*****************************************************************************/ +int32_t graphics_update_displayed_resource(GRAPHICS_RESOURCE_HANDLE res, + const uint32_t x_offset, + const uint32_t y_offset, + const uint32_t width, + const uint32_t height ) +{ + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + gx_priv_flush(res); + + gx_priv_restore(&save); + + return 0; +} + +/*****************************************************************************/ +int32_t graphics_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ) +{ + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + VCOS_STATUS_T st = gx_priv_resource_fill( + res, + x, res->height-y-height, + width, height, + fill_colour); + + gx_priv_restore(&save); + + return st == VCOS_SUCCESS ? 0 : -1; +} + +/*****************************************************************************/ +int32_t graphics_resource_render_text_ext( GRAPHICS_RESOURCE_HANDLE res, + const int32_t x, + const int32_t y, + const uint32_t width, + const uint32_t height, + const uint32_t fg_colour, + const uint32_t bg_colour, + const char *text, + const uint32_t text_length, + const uint32_t text_size ) +{ + + /* + * FIXME: Not at all optimal - re-renders each time. + * FIXME: Not UTF-8 safe + * FIXME: much better caching (or any caching) + */ + VCOS_STATUS_T rc = gx_priv_render_text( + &display, res, + x, res->height-y-text_size, width, height, fg_colour, bg_colour, + text, text_length, text_size); + + return (rc == VCOS_SUCCESS) ? 0 : -1; +} + +/*****************************************************************************/ +int32_t graphics_resource_render_text( GRAPHICS_RESOURCE_HANDLE res, + const int32_t x, + const int32_t y, + const uint32_t width, /* this can be GRAPHICS_RESOURCE_WIDTH for no clipping */ + const uint32_t height, /* this can be GRAPHICS_RESOURCE_HEIGHT for no clipping */ + const uint32_t fg_colour, + const uint32_t bg_colour, + const char *text, + const uint32_t text_length) +{ + return graphics_resource_render_text_ext(res, x, y, width, height, + fg_colour, bg_colour, + text, text_length, + ATEXT_FONT_SIZE); +} + +/*****************************************************************************/ +int32_t graphics_get_resource_size( + const GRAPHICS_RESOURCE_HANDLE res, + uint32_t *w, + uint32_t *h) +{ + if (w) *w = res->width; + if (h) *h = res->height; + return 0; +} + +/*****************************************************************************/ +int32_t graphics_get_resource_type(const GRAPHICS_RESOURCE_HANDLE res, GRAPHICS_RESOURCE_TYPE_T *type) +{ + if (type) *type = res->restype; + return 0; +} + +/*****************************************************************************/ +int32_t graphics_bitblt( const GRAPHICS_RESOURCE_HANDLE src, + const uint32_t x, // offset within source + const uint32_t y, // offset within source + const uint32_t width, + const uint32_t height, + GRAPHICS_RESOURCE_HANDLE dest, + const uint32_t x_pos, + const uint32_t y_pos ) +{ + int rc = -1; + VGfloat old[9]; + uint32_t w, h; + VGPaint paint = VG_INVALID_HANDLE; + GX_CLIENT_STATE_T save; + int is_child = 0; + VGImage img = VG_INVALID_HANDLE; + + gx_priv_save(&save, dest); + + if (src->type != GX_PBUFFER) + { + vcos_assert(0); + goto finish; + } + + // create a child image that contains just the part wanted + w = width == GRAPHICS_RESOURCE_WIDTH ? src->width : width; + h = height == GRAPHICS_RESOURCE_HEIGHT ? src->height : height; + + if (x==0 && y==0 && + w == src->width && + h == src->height) + { + img = src->u.pixmap; + } + else + { + is_child = 1; + img = vgChildImage(src->u.pixmap, x, y, w, h); + if (img == VG_INVALID_HANDLE) + { + vcos_assert(0); + goto finish; + } + } + + vcos_assert(vgGetError()==0); + + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgGetMatrix(old); + vgLoadIdentity(); + vgTranslate((VGfloat)x_pos, (VGfloat)(dest->height-y_pos)); + vgScale(1.0, -1.0); + + // Do we have a translucency going on? + if (src->alpha != 1.0) + { + VGfloat colour[4] = {1.0,1.0,1.0,src->alpha}; + paint = vgCreatePaint(); + + vgSetParameterfv(paint, VG_PAINT_COLOR, 4, colour); + vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_MULTIPLY); + vgSetPaint(paint, VG_STROKE_PATH | VG_FILL_PATH); + } + vcos_assert(vgGetError()==0); + + vgDrawImage(img); + vcos_assert(vgGetError()==0); + vgLoadMatrix(old); + + int err = vgGetError(); + + if (err) + { + GX_LOG("vg error %x blitting area", err); + vcos_assert(0); + rc = -1; + } + else + { + rc = 0; + } +finish: + if (paint != VG_INVALID_HANDLE) + vgDestroyPaint(paint); + + if (is_child) + vgDestroyImage(img); + + gx_priv_restore(&save); + return rc; +} + +void gx_priv_flush(GRAPHICS_RESOURCE_HANDLE res) +{ + EGLBoolean result; + result = eglSwapBuffers(display.disp, res->surface); + vcos_assert(result); +} + + +/** Map a colour, which the client will have supplied in RGB888. + */ + +void gx_priv_colour_to_paint(uint32_t col, VGfloat *rgba) +{ + // with OpenVG we use RGB order. + rgba[0] = ((VGfloat)((col & R_888_MASK) >> 16 )) / 0xff; + rgba[1] = ((VGfloat)((col & G_888_MASK) >> 8 )) / 0xff; + rgba[2] = ((VGfloat)((col & B_888_MASK) >> 0 )) / 0xff; + rgba[3] = ((VGfloat)((col & ALPHA_888_MASK) >> 24)) / 0xff; +} + +/** Fill an area of a surface with a fixed colour. + */ +VCOS_STATUS_T gx_priv_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ) +{ + VGfloat vg_clear_colour[4]; + + gx_priv_colour_to_paint(fill_colour, vg_clear_colour); + vgSeti(VG_SCISSORING, VG_FALSE); + + vgSetfv(VG_CLEAR_COLOR, 4, vg_clear_colour); + vgClear(x, y, width, height); + + int err = vgGetError(); + if (err) + { + GX_LOG("vg error %x filling area", err); + vcos_assert(0); + } + + return VCOS_SUCCESS; +} + +VCOS_STATUS_T gx_priv_get_pixels(const GRAPHICS_RESOURCE_HANDLE res, void **p_pixels, GX_RASTER_ORDER_T raster_order) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + void *pixels, *dest; + uint32_t width, height; + int data_size, pitch; + VGImageFormat image_format; + + if (!p_pixels) + { + status = VCOS_EINVAL; + goto finish; + } + + GX_TRACE("%s: res %p", __FUNCTION__, res); + + graphics_get_resource_size(res, &width, &height); + + /* FIXME: implement e.g. gx_get_pitch */ + switch (res->restype) + { + case GRAPHICS_RESOURCE_RGB565: + pitch = ((width + 31)&(~31)) << 1; + break; + case GRAPHICS_RESOURCE_RGB888: + case GRAPHICS_RESOURCE_RGBA32: + pitch = ((width + 31)&(~31)) << 2; + break; + default: + { + GX_LOG("Unsupported pixel format"); + status = VCOS_EINVAL; + goto finish; + } + } + + data_size = pitch * height; + + /* NB: vgReadPixels requires that the data pointer is aligned, but does not + require the stride to be aligned. Most implementations probably will + require that as well though... */ + pixels = vcos_malloc(data_size, "gx_get_pixels data"); + if (!pixels) + { + GX_LOG("Could not allocate %d bytes for vgReadPixels", data_size); + status = VCOS_ENOMEM; + goto finish; + } + /* FIXME: introduce e.g. GX_COLOR_FORMAT and mapping to VGImageFormat... */ + + /* Hand out image data formatted to match OpenGL RGBA format. + */ + switch (res->restype) + { + case GRAPHICS_RESOURCE_RGB565: + image_format = VG_sBGR_565; + break; + case GRAPHICS_RESOURCE_RGB888: + image_format = VG_sXBGR_8888; + break; + case GRAPHICS_RESOURCE_RGBA32: + image_format = VG_sABGR_8888; + break; + default: + { + GX_LOG("Unsupported pixel format"); + status = VCOS_EINVAL; + goto finish; + } + } + + /* VG raster order is bottom-to-top */ + if (raster_order == GX_TOP_BOTTOM) + { + dest = ((uint8_t*)pixels)+(pitch*(height-1)); + pitch = -pitch; + } + else + { + dest = pixels; + } + + vgReadPixels(dest, pitch, image_format, 0, 0, width, height); + + vcos_assert(vgGetError() == 0); + + *p_pixels = pixels; + +finish: + return status; +} + +static VCOS_STATUS_T convert_image_type(GRAPHICS_RESOURCE_TYPE_T image_type, + VGImageFormat *vg_image_type, + int *pbytes_per_pixel) +{ + int bytes_per_pixel; + + switch (image_type) + { + case GRAPHICS_RESOURCE_RGB565: + *vg_image_type = VG_sRGB_565; + bytes_per_pixel = 2; + break; + case GRAPHICS_RESOURCE_RGB888: + *vg_image_type = VG_sRGBX_8888; + bytes_per_pixel = 3; // 24 bpp + break; + case GRAPHICS_RESOURCE_RGBA32: + *vg_image_type = VG_sARGB_8888; + bytes_per_pixel = 4; + break; + default: + vcos_assert(0); + *vg_image_type = 0; + return VCOS_EINVAL; + } + if (pbytes_per_pixel) + *pbytes_per_pixel = bytes_per_pixel; + + return VCOS_SUCCESS; +} + + +/*****************************************************************************/ +VCOS_STATUS_T gx_create_pbuffer( uint32_t width, + uint32_t height, + GRAPHICS_RESOURCE_TYPE_T image_type, + GRAPHICS_RESOURCE_HANDLE *resource_handle ) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + GRAPHICS_RESOURCE_HANDLE h; + VGImage image; + VGImageFormat vg_image_type; + GX_CLIENT_STATE_T save; + + h = vcos_calloc(1,sizeof(*h), "graphics_x_resource"); + if (!h) + { + GX_LOG("%s: no memory for resource", __FUNCTION__); + return VCOS_ENOMEM; + } + + status = convert_image_type(image_type, &vg_image_type, NULL); + if (status != VCOS_SUCCESS) + { + vcos_free(h); + return status; + } + + h->magic = RES_MAGIC; + h->context = gx_contexts[image_type]; + h->config = gx_configs[image_type]; + h->alpha = 1.0; + h->type = GX_PBUFFER; + h->width = width; + h->height = height; + h->restype = image_type; + + GX_TRACE("Creating pbuffer surface"); + + EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE}; + h->surface = eglCreatePbufferSurface(display.disp, h->config, + attribs); + if (!h->surface) + { + GX_LOG("Could not create EGL pbuffer surface: 0x%x", eglGetError()); + vcos_free(h); + return VCOS_EINVAL; + } + + gx_priv_save(&save, h); + + image = vgCreateImage(vg_image_type, width, height, VG_IMAGE_QUALITY_BETTER); + if (image == VG_INVALID_HANDLE) + { + GX_LOG("Could not create vg image type %d: vg error 0x%x", + vg_image_type, vgGetError()); + eglDestroySurface(display.disp, h->surface); + vcos_free(h); + status = VCOS_ENOMEM; + goto finish; + } + + h->u.pixmap = image; + + // fill it with black + status = gx_priv_resource_fill(h, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0xff)); + vcos_assert(status == VCOS_SUCCESS); + + *resource_handle = h; +finish: + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +GX_PAINT_T *gx_create_gradient(GRAPHICS_RESOURCE_HANDLE res, + uint32_t start_colour, + uint32_t end_colour) +{ + // holds the two colour stops (offset,r,g,b,a). + VGfloat fill_stops[10]; + GX_CLIENT_STATE_T save; + VGPaint paint = VG_INVALID_HANDLE; + + gx_priv_save(&save, res); + + paint = vgCreatePaint(); + if (!paint) + { + gx_priv_restore(&save); + vcos_log("Could not create paint: vg %d\n", vgGetError()); + vcos_assert(0); + goto finish; + } + + fill_stops[0] = 0.0; + gx_priv_colour_to_paint(start_colour, fill_stops+1); + + fill_stops[5] = 1.0; + gx_priv_colour_to_paint(end_colour, fill_stops+6); + + vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT); + vgSetParameterfv(paint, VG_PAINT_COLOR_RAMP_STOPS, 5*2, fill_stops); + +finish: + gx_priv_restore(&save); + return (GX_PAINT_T*)paint; +} + +/*****************************************************************************/ +void gx_destroy_paint(GRAPHICS_RESOURCE_HANDLE res, GX_PAINT_T *p) +{ + GX_CLIENT_STATE_T save; + VGPaint paint = (VGPaint)p; + gx_priv_save(&save, res); + vgDestroyPaint(paint); + gx_priv_restore(&save); +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_fill_gradient(GRAPHICS_RESOURCE_HANDLE dest, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height, + uint32_t radius, + GX_PAINT_T *p) +{ + /* Define start and end points of gradient, see OpenVG specification, + section 9.3.3. */ + VGfloat gradient[4] = {0.0, 0.0, 0.0, 0.0}; + VGPaint paint = (VGPaint)p; + VGPath path; + GX_CLIENT_STATE_T save; + VCOS_STATUS_T status = VCOS_SUCCESS; + + if (!paint) + return VCOS_EINVAL; + + gx_priv_save(&save, dest); + + if (width == GRAPHICS_RESOURCE_WIDTH) + width = dest->width; + + if (height == GRAPHICS_RESOURCE_HEIGHT) + height = dest->height; + + gradient[2] = width; + + vgSetParameterfv(paint, VG_PAINT_LINEAR_GRADIENT, 4, gradient); + vgSetPaint(paint, VG_FILL_PATH); + + path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_S_32, + 1.0, 0.0, 8, 8, VG_PATH_CAPABILITY_ALL); + if (!path) + { + status = VCOS_ENOMEM; + goto finish; + } + + vguRoundRect(path, (VGfloat)x, (VGfloat)y, (VGfloat)width, (VGfloat)height, + (VGfloat)radius, (VGfloat)radius); + vgDrawPath(path, VG_FILL_PATH); + vgDestroyPath(path); + + vcos_assert(vgGetError() == 0); + +finish: + gx_priv_restore(&save); + + return status; +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_graphics_init(const char *font_dir) +{ + GX_CLIENT_STATE_T save; + VCOS_STATUS_T rc; + + gx_priv_save(&save, NULL); + + rc = gx_priv_initialise(); + if (rc == VCOS_SUCCESS) + rc = gx_priv_font_init(font_dir); + + gx_priv_restore(&save); + + return rc; +} + +/*****************************************************************************/ +int gx_is_double_buffered(void) +{ + return 1; +} + +/*****************************************************************************/ +int32_t graphics_userblt(GRAPHICS_RESOURCE_TYPE_T src_type, + const void *src_data, + const uint32_t src_x, + const uint32_t src_y, + const uint32_t width, + const uint32_t height, + const uint32_t pitch, + GRAPHICS_RESOURCE_HANDLE dest, + const uint32_t x_pos, + const uint32_t y_pos ) +{ + VCOS_STATUS_T status; + VGImageFormat vg_src_type; + int bytes_per_pixel; + GX_CLIENT_STATE_T save; + + status = convert_image_type(src_type, &vg_src_type, &bytes_per_pixel); + if (status != VCOS_SUCCESS) + return status; + + gx_priv_save(&save, dest); + + if (dest->type == GX_PBUFFER) + { + vgImageSubData(dest->u.pixmap, + src_data, + pitch, + vg_src_type, + x_pos, y_pos, width, height); + } + else if (dest->type == GX_WINDOW) + { + // need to invert this as VG thinks zero is at the bottom + // while graphics_x thinks it is at the top. + vgWritePixels((uint8_t*)src_data + pitch*(height-1), + -pitch, + vg_src_type, + x_pos, dest->height-y_pos-height, width, height); + } + else + { + vcos_assert(0); + } + + if (vgGetError() == 0) + status = VCOS_SUCCESS; + else + { + vcos_assert(0); + status = VCOS_EINVAL; + } + + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +int32_t graphics_resource_text_dimensions( GRAPHICS_RESOURCE_HANDLE resource_handle, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height ) +{ + return graphics_resource_text_dimensions_ext(resource_handle, text, text_length, width, height, ATEXT_FONT_SIZE); +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_render_arrowhead(GRAPHICS_RESOURCE_HANDLE res, + uint32_t tip_x, uint32_t tip_y, + int32_t w, int32_t h, + GX_PAINT_T *p) +{ + VGfloat gradient[4]; + VGPaint paint = (VGPaint)p; + VGPath path; + VCOS_STATUS_T status = VCOS_SUCCESS; + + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + if (!paint) + { + vcos_assert(0); + status = VCOS_EINVAL; + goto finish; + } + + gradient[0] = 0.0; gradient[1] = 0.0; + gradient[2] = w; gradient[2] = 0.0; + + vgSetParameterfv(paint, VG_PAINT_LINEAR_GRADIENT, 4, gradient); + vgSetPaint(paint, VG_FILL_PATH); + + path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_S_32, + 1.0, 0.0, 8, 8, VG_PATH_CAPABILITY_ALL); + if (!path) + { + status = VCOS_ENOMEM; + goto finish; + } + VGfloat points[] = { + (VGfloat)tip_x, (VGfloat)tip_y, + (VGfloat)tip_x + w, (VGfloat)tip_y + h/2, + (VGfloat)tip_x + w, (VGfloat)tip_y - h/2, + }; + + vguPolygon(path, points, 3, 1); + + vgDrawPath(path, VG_FILL_PATH); + vgDestroyPath(path); + + vcos_assert(vgGetError()==0); + +finish: + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +int32_t gx_apply_alpha( GRAPHICS_RESOURCE_HANDLE resource_handle, + const uint8_t alpha ) +{ + vcos_assert(resource_handle); + if (resource_handle->type != GX_PBUFFER) + { + vcos_assert(0); + return -1; + } + resource_handle->alpha = 1.0*alpha/255; + return 0; +} + +/*****************************************************************************/ +int32_t graphics_resource_set_alpha_per_colour( GRAPHICS_RESOURCE_HANDLE res, + const uint32_t colour, + const uint8_t alpha ) +{ + GX_ERROR("Not implemented yet!"); + return 0; +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_get_pixels(const GRAPHICS_RESOURCE_HANDLE res, void **pixels) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + /* Default to top-top-bottom raster scan order */ + status = gx_priv_get_pixels(res, pixels, GX_TOP_BOTTOM); + + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_get_pixels_in_raster_order(const GRAPHICS_RESOURCE_HANDLE res, + void **pixels, + GX_RASTER_ORDER_T raster_order) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + status = gx_priv_get_pixels(res, pixels, raster_order); + + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +void gx_free_pixels(const GRAPHICS_RESOURCE_HANDLE res, void *pixels) +{ + vcos_free(pixels); +} + +VCOS_STATUS_T gx_bind_vg( GX_CLIENT_STATE_T *save, GRAPHICS_RESOURCE_HANDLE res ) +{ + gx_priv_save(save, res); + vcos_assert(vgGetError()==0); + return VCOS_SUCCESS; +} + +/** Unbind VG */ +void gx_unbind_vg(GX_CLIENT_STATE_T *restore) +{ + gx_priv_restore(restore); +} + + +GX_CLIENT_STATE_T *gx_alloc_context(void) +{ + GX_CLIENT_STATE_T *ret = vcos_calloc(1,sizeof(*ret), "gx_client_state"); + return ret; +} + +void gx_free_context(GX_CLIENT_STATE_T *state) +{ + vcos_free(state); +} + +void gx_convert_colour(uint32_t colour, float *dest) +{ + gx_priv_colour_to_paint(colour, dest); +} + + +#define MAX_DISPLAY_HANDLES 4 + +#define CHANGE_LAYER (1<<0) +#define CHANGE_OPACITY (1<<1) +#define CHANGE_DEST (1<<2) +#define CHANGE_SRC (1<<3) +#define CHANGE_MASK (1<<4) +#define CHANGE_XFORM (1<<5) + +typedef struct +{ + /** Keep a display handle going for each connected screen (LCD, HDMI). */ + DISPMANX_DISPLAY_HANDLE_T screens[MAX_DISPLAY_HANDLES]; + int refcounts[MAX_DISPLAY_HANDLES]; + + //a flag to count the number of dispman starts that have been invoked + + uint32_t dispman_start_count; + // maintain the single global handle to the update in progress + DISPMANX_UPDATE_HANDLE_T current_update; + + VCOS_MUTEX_T lock; +} gx_priv_state_t; + +static gx_priv_state_t gx; + +void gx_priv_init(void) +{ + vcos_mutex_create(&gx.lock,NULL); +} + +void gx_priv_destroy(void) +{ + vcos_mutex_delete(&gx.lock); +} + + +static +int32_t gx_priv_open_screen(uint32_t index, DISPMANX_DISPLAY_HANDLE_T *pscreen) +{ + int ret = -1; + vcos_mutex_lock(&gx.lock); + + if (gx.refcounts[index] != 0) + { + *pscreen = gx.screens[index]; + gx.refcounts[index]++; + ret = 0; + } + else + { + DISPMANX_DISPLAY_HANDLE_T h = vc_dispmanx_display_open(index); + if (h == DISPMANX_NO_HANDLE) + { + GX_LOG("Could not open dispmanx display %d", index); + ret = -1; + goto finish; + } + gx.screens[index] = h; + gx.refcounts[index] = 1; + *pscreen = h; + ret = 0; + } +finish: + vcos_mutex_unlock(&gx.lock); + return ret; +} + +static +int32_t gx_priv_release_screen(uint32_t index) +{ + vcos_mutex_lock(&gx.lock); + gx.refcounts[index]--; + if (gx.refcounts[index] == 0) + { + vc_dispmanx_display_close(gx.screens[index]); + gx.screens[index] = DISPMANX_NO_HANDLE; + } + vcos_mutex_unlock(&gx.lock); + return 0; +} + + + + +int gx_priv_create_native_window(uint32_t screen_id, + uint32_t w, uint32_t h, + GRAPHICS_RESOURCE_TYPE_T type, + GX_NATIVE_WINDOW_T *win, + void **cookie) +{ + int rc; + DISPMANX_DISPLAY_HANDLE_T dispmanx_display; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + DISPMANX_UPDATE_HANDLE_T current_update; + *cookie = NULL; + + rc = gx_priv_open_screen(screen_id, &dispmanx_display); + if (rc < 0) + { + GX_LOG("Could not open display %d", screen_id); + goto fail_screen; + } + + current_update = vc_dispmanx_update_start(0); + if (!current_update) + { + GX_LOG("Could not start update on screen %d", screen_id); + goto fail_update; + } + + src_rect.x = src_rect.y = 0; + src_rect.width = w << 16; + src_rect.height = h << 16; + + dst_rect.x = dst_rect.y = 0; + dst_rect.width = dst_rect.height = 1; + + win->egl_win.width = w; + win->egl_win.height = h; + VC_DISPMANX_ALPHA_T alpha; + memset(&alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T)); + alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE; + + DISPMANX_CLAMP_T clamp; + memset(&clamp, 0x0, sizeof(DISPMANX_CLAMP_T)); + + win->egl_win.element = vc_dispmanx_element_add(current_update, dispmanx_display, + 0 /* layer */, &dst_rect, + 0 /* src */, &src_rect, + DISPMANX_PROTECTION_NONE, + &alpha /* alpha */, + &clamp /* clamp */, + 0 /* transform */); + + if ( !win->egl_win.element ) + { + GX_LOG("Could not add element %dx%d",w,h); + vc_dispmanx_update_submit_sync(current_update); + rc = -1; + } + + // have to pass back the update so it can be completed *After* the + // window has been initialised (filled with background colour). + *cookie = (void*)current_update; + + return 0; + +fail_update: + gx_priv_release_screen(screen_id); +fail_screen: + return rc; +} + +void gx_priv_finish_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res, + void *current_update) +{ + vc_dispmanx_update_submit_sync((DISPMANX_UPDATE_HANDLE_T)current_update); +} + +void +gx_priv_destroy_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res) +{ + DISPMANX_UPDATE_HANDLE_T current_update; + + if((current_update = vc_dispmanx_update_start(0)) != 0) + { + int ret = vc_dispmanx_element_remove(current_update, res->u.native_window.egl_win.element); + vcos_assert(ret == 0); + ret = vc_dispmanx_update_submit_sync(current_update); + vcos_assert(ret == 0); + } + + gx_priv_release_screen(res->screen_id); +} + + +/*********************************************************** + * Name: graphics_get_display_size + * + * Arguments: + * void + * + * Description: Return size of display + * + * Returns: int32_t: + * >=0 if it succeeded + * + ***********************************************************/ +int32_t graphics_get_display_size( const uint16_t display_number, + uint32_t *width, + uint32_t *height) +{ + DISPMANX_MODEINFO_T mode_info; + int32_t success = -1; + DISPMANX_DISPLAY_HANDLE_T disp; + vcos_assert(width && height); + *width = *height = 0; + + if(vcos_verify(display_number < MAX_DISPLAY_HANDLES)) + { + // TODO Shouldn't this close the display if it wasn't previously open? + if (gx_priv_open_screen(display_number, &disp) < 0) + { + vcos_assert(0); + return -1; + } + success = vc_dispmanx_display_get_info(disp, &mode_info); + + if( success >= 0 ) + { + *width = mode_info.width; + *height = mode_info.height; + vcos_assert(*height > 64); + } + else + { + vcos_assert(0); + } + } + + return success; +} + +static inline uint16_t auto_size(uint16_t arg, uint16_t actual_size) +{ + return arg == GRAPHICS_RESOURCE_WIDTH ? actual_size : arg; +} + +int32_t graphics_display_resource( GRAPHICS_RESOURCE_HANDLE res, + const uint16_t screen_number, + const int16_t z_order, + const uint16_t offset_x, + const uint16_t offset_y, + const uint16_t dest_width, + const uint16_t dest_height, + const VC_DISPMAN_TRANSFORM_T transform, + const uint8_t display ) +{ + DISPMANX_UPDATE_HANDLE_T update; + int32_t rc; + int xform_changed; + + if (!res) + { + // mimics old behaviour. + (void)vcos_verify(0); + return 0; + } + vcos_assert(res->magic == RES_MAGIC); + + xform_changed = transform != res->transform; + res->transform = transform; + + rc = graphics_update_start(); + update = gx.current_update; + vcos_assert(rc == 0); + + if (display) + { + VC_RECT_T src_rect, dest_rect; + + int32_t src_width = res->width; + int32_t src_height = res->height; + + uint32_t change_flags = CHANGE_LAYER; + + // has the destination position changed? + uint32_t w = auto_size(dest_width, res->width); + uint32_t h = auto_size(dest_height, res->height); + + vcos_assert(screen_number == res->screen_id); + + if (gx.screens[screen_number] == 0) + { + vcos_assert(0); + DISPMANX_DISPLAY_HANDLE_T display_handle; + gx_priv_open_screen(screen_number, &display_handle); + } + + if ((offset_x != res->dest.x) || + (offset_y != res->dest.y) || + (h != res->dest.height) || + (w != res->dest.width)) + { + change_flags |= CHANGE_DEST; + res->dest.x = offset_x; + res->dest.y = offset_y; + res->dest.height = h; + res->dest.width = w; + } + + if (xform_changed) + change_flags |= CHANGE_XFORM; + + vc_dispmanx_rect_set( &src_rect, 0, 0, ((uint32_t)src_width)<<16, ((uint32_t)src_height)<<16 ); + vc_dispmanx_rect_set( &dest_rect, offset_x, offset_y, w, h); + + rc = vc_dispmanx_element_change_attributes(update, + res->u.native_window.egl_win.element, + change_flags, + z_order, /* layer */ + 0xff, /* opacity */ + &dest_rect, + &src_rect, + 0, transform); + + vcos_assert(rc==0); + gx_priv_flush(res); + + } + else + { + vgFinish(); + eglWaitClient(); + rc = vc_dispmanx_element_change_source(update, res->u.native_window.egl_win.element, 0); + vcos_assert(rc==0); + } + + rc = graphics_update_end(); + vcos_assert(rc==0); + + return rc; +} + +/*********************************************************** + * Name: graphics_update_start + * + * Arguments: + * void + * + * Description: Starts an update UNLESS and update is already in progress + * + * Returns: int32_t: + * >=0 if it succeeded + * + ***********************************************************/ +int32_t graphics_update_start(void) +{ + int32_t success = 0; + + //check we are not already in an update + if ( 0 == gx.dispman_start_count ) + { + gx.current_update = vc_dispmanx_update_start( 10 ); + if( gx.current_update == DISPMANX_NO_HANDLE ) + { + //error + success = -1; + vc_assert( 0 ); + } + } + + if( success == 0 ) + { + //inc the counter + gx.dispman_start_count++; + } + + return success; +} + + +/*********************************************************** + * Name: graphics_update_end + * + * Arguments: + * void + * + * Description: Ends an update UNLESS more than one update is in progress + * + * Returns: int32_t: + * >=0 if it succeeded + * + ***********************************************************/ +int32_t graphics_update_end( void ) +{ + int32_t success = -1; + + // make sure you are checking the return value of graphics_update_start + if(vcos_verify(gx.current_update != DISPMANX_NO_HANDLE)) + { + //check we are in an update + if(vcos_verify(gx.dispman_start_count > 0)) + { + //dec the counter + gx.dispman_start_count--; + + success = 0; + + //is the counter now 0? + if( 0 == gx.dispman_start_count ) + { + eglWaitClient(); + if( vc_dispmanx_update_submit_sync( gx.current_update ) != 0 ) + { + //error + success = -1; + vc_assert( 0 ); + } + } + } + } + + return success; +} + diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/graphics_x_private.h b/host_applications/linux/apps/hello_pi/libs/vgfont/graphics_x_private.h new file mode 100755 index 0000000..05d94b7 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/graphics_x_private.h @@ -0,0 +1,366 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Graphics library for VG + +#ifndef GRAPHICS_X_PRIVATE_H +#define GRAPHICS_X_PRIVATE_H + +#define VCOS_LOG_CATEGORY (&gx_log_cat) + +#include "EGL/egl.h" +#include "EGL/eglext.h" +#include "VG/openvg.h" +#include "VG/vgu.h" + +#include "vgfont.h" +#include "bcm_host.h" + +extern VCOS_LOG_CAT_T gx_log_cat; + +#define LOG_ERR( fmt, arg... ) vcos_log_error( "%s:%d " fmt, __func__, __LINE__, ##arg) + +#define GX_ERROR(format, arg...) if (1) {} else printf( format "\n", ##arg) +#define GX_LOG(format, arg...) if (1) {} else printf( format "\n", ##arg) +#define GX_TRACE(format, arg...) if (1) {} else printf( format "\n", ##arg) + +typedef struct +{ + EGL_DISPMANX_WINDOW_T egl_win; +} GX_NATIVE_WINDOW_T; + +typedef enum +{ + GX_TOP_BOTTOM, + GX_BOTTOM_TOP, +} GX_RASTER_ORDER_T; + +typedef struct {} GX_PAINT_T; + +typedef struct GX_CLIENT_STATE_T GX_CLIENT_STATE_T; +typedef struct { + EGLDisplay disp; +} GX_DISPLAY_T; + +struct GX_DISPLAY_T +{ + EGLDisplay disp; +}; + +typedef enum +{ + GX_WINDOW, GX_PIXMAP, GX_PBUFFER +} GX_RES_TYPE; + +#define RES_MAGIC ('G'<<24|'X'<<16|'R'<<8|'S'<<0) +#define GX_PRIV_FLAG_FLIP (1<<0) + +/** + * Structure encapsulating the per-surface state. + ***********************************************************/ +typedef struct GRAPHICS_RESOURCE_HANDLE_TABLE_T +{ + union + { + GX_NATIVE_WINDOW_T native_window; + VGImage pixmap; + } u; + GX_RES_TYPE type; + + uint32_t magic; /** To work around broken create interface */ + int context_bound; + const char *last_caller; + EGLSurface surface; + EGLContext context; + EGLConfig config; + uint32_t screen_id; /** 0-LCD, etc */ + uint16_t width; + uint16_t height; + GRAPHICS_RESOURCE_TYPE_T restype; + VC_DISPMAN_TRANSFORM_T transform; + + VC_RECT_T dest; /** destination rectangle in use, for book-keeping */ + + VGfloat alpha; +} GRAPHICS_RESOURCE_HANDLE_TABLE_T; + +/** + * Structure used to store an EGL client state. + ***********************************************************/ +struct GX_CLIENT_STATE_T +{ + EGLSurface read_surface; + EGLSurface draw_surface; + EGLContext context; + EGLenum api; + GRAPHICS_RESOURCE_HANDLE res; +}; + +/** + * \fixme add documentation + * + ***********************************************************/ +void gx_priv_init(void); + +/** + * \fixme add documentation + * + ***********************************************************/ +void gx_priv_destroy(void); + +/** + * \fixme add documentation + * + * @param col colour + * + * @param rgba OpenVG paint colour + * + ***********************************************************/ +void gx_priv_colour_to_paint(uint32_t col, VGfloat *rgba); + +/** + * Save current EGL client state. + * + * @param state upon return, holds the saved EGL client state. + * + * @param res handle to the surface the EGL client state belongs to (may be NULL). + * + */ +void gx_priv_save(GX_CLIENT_STATE_T *state, GRAPHICS_RESOURCE_HANDLE res); + +/** + * Restore current EGL client state. + * + * @param state the EGL client state to restore. + * + */ +void gx_priv_restore(GX_CLIENT_STATE_T *state); + +/** + * Create a native window for a surface. + * + * @param screen_id \fixme + * + * @param w width of the window + * + * @param h height of the window + * + * @param type color/raster format of the resource + * + * @param win upon successful return, holds a handle to the native window + * + * @param cookie \fixme + * + * @return VCOS_SUCCESS on success, or error code. + */ +int gx_priv_create_native_window(uint32_t screen_id, + uint32_t w, uint32_t h, + GRAPHICS_RESOURCE_TYPE_T type, + GX_NATIVE_WINDOW_T *win, + void **cookie); + +/** + * Destroy native window bound to surface. + * + * @param res Handle to surface. + * + */ +void gx_priv_destroy_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res); + +/** + * Initialise font from the given directory. + * + * @param font_dir path to font + * + * \fixme only supports Vera.tff at the moment? + * + * @return VCOS_SUCCESS on success, or error code. + */ +VCOS_STATUS_T gx_priv_font_init(const char *font_dir); + +/** + * \fixme add documentation + * + ***********************************************************/ +void gx_priv_font_term(void); + +/** + * Fill an area of a surface with a single colour. + * + * @param res Handle to surface. + * + * @param x x-offset of area to fill + * + * @param y y-offset of area to fill + * + * @param width width of area to fill + * + * @param height height of area to fill + * + * @param fill_colour fill colour + * + ***********************************************************/ +VCOS_STATUS_T gx_priv_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ); + +/** + * Render text into a surface + * + * @param disp Handle to display. + * + * @param res Handle to surface. + * + * @param x x-offset + * + * @param y y-offset + * + * @param width bounding rectangle width + * + * @param height bounding rectangle height + * + * @param fg_colour foreground color + * + * @param bg_colour background color + * + * @param text text to render + * + * @param text_length length of text + * + * @param text_size size of text + * + ***********************************************************/ +VCOS_STATUS_T gx_priv_render_text( GX_DISPLAY_T *disp, + GRAPHICS_RESOURCE_HANDLE res, + int32_t x, + int32_t y, + uint32_t width, + uint32_t height, + uint32_t fg_colour, + uint32_t bg_colour, + const char *text, + uint32_t text_length, + uint32_t text_size ); + +/** + * Flush a surface. + * + * @param res Handle to surface. + * + ***********************************************************/ +void gx_priv_flush(GRAPHICS_RESOURCE_HANDLE res); + +/** + * Called after the EGL/VG initialisation of a window has completed + * following its creation. + * + * @param res ??? + * + * @param cookie ??? + * + ***********************************************************/ +void gx_priv_finish_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res, + void *cookie); + +/** + * Flush font cache. + * + ***********************************************************/ +void gx_font_cache_flush(void); + +/** + * Read a bitmap (.BMP) image from the given file. + * + * @param filename filename (must not be NULL). + * + * @param width holds the width of the image upon return. + * + * @param height holds the height of the image upon return. + * + * @param pitch_bytes holds the pitch of the image data (in bytes) upon return. + * + * @param restype holds the type of the image upon return. + * + * @param vg_format holds the OpenVG image format upon return. + * + * @param flags holds flags describing image properties upon return. + * + * @param image_data_size holds size of the image data upon return. + * + * @param pimage_data holds the image data buffer upon return (must not be NULL), + * the caller is responsible for releasing the buffer afterwards. + * + * @return 0 if success, non-zero otherwise (in which case the output parameters + * may be invalid). + * + ***********************************************************/ +int gx_priv_read_bmp(const char *file_name, + uint32_t *width, uint32_t *height, uint32_t *pitch_bytes, + GRAPHICS_RESOURCE_TYPE_T *restype, + VGImageFormat *vg_format, + uint32_t *flags, + uint32_t *image_data_size, + void **pimage_data); + +/** + * Read a Targa (.TGA) image from the given file. + * + * @param filename filename (must not be NULL). + * + * @param width holds the width of the image upon return. + * + * @param height holds the height of the image upon return. + * + * @param pitch_bytes holds the pitch of the image data (in bytes) upon return. + * + * @param restype holds the type of the image upon return. + * + * @param vg_format holds the OpenVG image format upon return. + * + * @param flags holds flags describing image properties upon return. + * + * @param image_data_size holds size of the image data upon return. + * + * @param pimage_data holds the image data buffer upon return (must not be NULL), + * the caller is responsible for releasing the memory afterwards. + * + * @return 0 if success, non-zero otherwise (in which case the output parameters. + * may be invalid). + * + ***********************************************************/ +int gx_priv_read_tga(const char *file_name, + uint32_t *width, uint32_t *height, uint32_t *pitch_bytes, + GRAPHICS_RESOURCE_TYPE_T *restype, + VGImageFormat *vg_format, + uint32_t *flags, + uint32_t *image_data_size, + void **pimage_data); + +#endif diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/vgfont.h b/host_applications/linux/apps/hello_pi/libs/vgfont/vgfont.h new file mode 100755 index 0000000..b20bb55 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/vgfont.h @@ -0,0 +1,136 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +#ifndef VCFTLIB_H +#define VCFTLIB_H + +#include +#include "interface/vmcs_host/vc_dispservice_x_defs.h" +#include "interface/vctypes/vc_image_types.h" +#include "interface/vcos/vcos.h" + +//Definitions which in certain functions can be used to mean the actual width and height of a resource, without +//having to know the data implicitly. +#define GRAPHICS_RESOURCE_WIDTH 0xFFFF +#define GRAPHICS_RESOURCE_HEIGHT 0xFFFF + +#define R_888_MASK (0x00FF0000) +#define G_888_MASK (0x0000FF00) +#define B_888_MASK (0x000000FF) +#define ALPHA_888_MASK (0xFF000000) +#define GRAPHICS_RGBA32( r, g, b, a ) GRAPHICS_RGBA888( r, g, b, a ) +#define GRAPHICS_RGBA888( r, g, b, a ) ( (((a) << (8+8+8)) & ALPHA_888_MASK) | (((b) << (8+8)) & R_888_MASK) | (((g) << 8) & G_888_MASK) | ((r) & B_888_MASK) ) +#define GRAPHICS_TRANSPARENT_COLOUR 0x00000001UL + +//resource defs + +typedef enum +{ + GRAPHICS_RESOURCE_HANDLE_TYPE_MIN, + + GRAPHICS_RESOURCE_RGB565, + GRAPHICS_RESOURCE_RGB888, /* 888 format is ONLY used when loading bitmaps + - you can't create or delete bitmaps with this format */ + GRAPHICS_RESOURCE_RGBA32, + GRAPHICS_RESOURCE_TF_RGB32A, + GRAPHICS_RESOURCE_TF_RGB565, + GRAPHICS_RESOURCE_YUV420, + + GRAPHICS_RESOURCE_HANDLE_TYPE_MAX + +} GRAPHICS_RESOURCE_TYPE_T; + + +typedef struct GRAPHICS_RESOURCE_HANDLE_TABLE_T *GRAPHICS_RESOURCE_HANDLE; + +VCOS_STATUS_T gx_graphics_init(const char *font_dir); +int32_t graphics_delete_resource( GRAPHICS_RESOURCE_HANDLE res ); +VCOS_STATUS_T gx_create_window( uint32_t screen_id, + uint32_t width, + uint32_t height, + GRAPHICS_RESOURCE_TYPE_T image_type, + GRAPHICS_RESOURCE_HANDLE *resource_handle ); + +int32_t graphics_display_resource( GRAPHICS_RESOURCE_HANDLE res, + const uint16_t screen_number, + const int16_t z_order, + const uint16_t offset_x, + const uint16_t offset_y, + const uint16_t dest_width, + const uint16_t dest_height, + const VC_DISPMAN_TRANSFORM_T transform, + const uint8_t display ); + +int32_t graphics_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ); + +int32_t graphics_update_displayed_resource(GRAPHICS_RESOURCE_HANDLE res, + const uint32_t x_offset, + const uint32_t y_offset, + const uint32_t width, + const uint32_t height ); + +int32_t graphics_resource_render_text_ext( GRAPHICS_RESOURCE_HANDLE res, + const int32_t x, + const int32_t y, + const uint32_t width, + const uint32_t height, + const uint32_t fg_colour, + const uint32_t bg_colour, + const char *text, + const uint32_t text_length, + const uint32_t text_size ); + +int32_t graphics_resource_text_dimensions_ext(GRAPHICS_RESOURCE_HANDLE res, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height, + const uint32_t text_size ); + +int32_t graphics_get_resource_size( + const GRAPHICS_RESOURCE_HANDLE res, + uint32_t *w, + uint32_t *h); + +int32_t graphics_update_start(void); + +int32_t graphics_update_end( void ); + +int32_t graphics_resource_text_dimensions( GRAPHICS_RESOURCE_HANDLE resource_handle, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height ); + +#endif diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.c b/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.c new file mode 100755 index 0000000..53104c3 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.c @@ -0,0 +1,424 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +#include +#include + +#include "graphics_x_private.h" +#include "vgft.h" + +static FT_Library lib; + +int vgft_init(void) +{ + if (FT_Init_FreeType(&lib) == 0) + return 0; + else + { + return -1; + } +} + +void vgft_term(void) +{ + FT_Done_FreeType(lib); +} + +#define SEGMENTS_COUNT_MAX 256 +#define COORDS_COUNT_MAX 1024 + +static VGuint segments_count; +static VGubyte segments[SEGMENTS_COUNT_MAX]; +static VGuint coords_count; +static VGfloat coords[COORDS_COUNT_MAX]; + +static VGfloat float_from_26_6(FT_Pos x) +{ + return (VGfloat)x / 64.0f; +} + +static void convert_contour(const FT_Vector *points, const char *tags, short points_count) +{ + int first_coords = coords_count; + + int first = 1; + char last_tag = 0; + int c = 0; + + for (; points_count != 0; ++points, ++tags, --points_count) { + ++c; + + char tag = *tags; + if (first) { + assert(tag & 0x1); + assert(c==1); c=0; + segments[segments_count++] = VG_MOVE_TO; + first = 0; + } else if (tag & 0x1) { + /* on curve */ + + if (last_tag & 0x1) { + /* last point was also on -- line */ + assert(c==1); c=0; + segments[segments_count++] = VG_LINE_TO; + } else { + /* last point was off -- quad or cubic */ + if (last_tag & 0x2) { + /* cubic */ + assert(c==3); c=0; + segments[segments_count++] = VG_CUBIC_TO; + } else { + /* quad */ + assert(c==2); c=0; + segments[segments_count++] = VG_QUAD_TO; + } + } + } else { + /* off curve */ + + if (tag & 0x2) { + /* cubic */ + + assert((last_tag & 0x1) || (last_tag & 0x2)); /* last either on or off and cubic */ + } else { + /* quad */ + + if (!(last_tag & 0x1)) { + /* last was also off curve */ + + assert(!(last_tag & 0x2)); /* must be quad */ + + /* add on point half-way between */ + assert(c==2); c=1; + segments[segments_count++] = VG_QUAD_TO; + VGfloat x = (coords[coords_count - 2] + float_from_26_6(points->x)) * 0.5f; + VGfloat y = (coords[coords_count - 1] + float_from_26_6(points->y)) * 0.5f; + coords[coords_count++] = x; + coords[coords_count++] = y; + } + } + } + last_tag = tag; + + coords[coords_count++] = float_from_26_6(points->x); + coords[coords_count++] = float_from_26_6(points->y); + } + + if (last_tag & 0x1) { + /* last point was also on -- line (implicit with close path) */ + assert(c==0); + } else { + ++c; + + /* last point was off -- quad or cubic */ + if (last_tag & 0x2) { + /* cubic */ + assert(c==3); c=0; + segments[segments_count++] = VG_CUBIC_TO; + } else { + /* quad */ + assert(c==2); c=0; + segments[segments_count++] = VG_QUAD_TO; + } + + coords[coords_count++] = coords[first_coords + 0]; + coords[coords_count++] = coords[first_coords + 1]; + } + + segments[segments_count++] = VG_CLOSE_PATH; +} + +static void convert_outline(const FT_Vector *points, const char *tags, const short *contours, short contours_count, short points_count) +{ + segments_count = 0; + coords_count = 0; + + short last_contour = 0; + for (; contours_count != 0; ++contours, --contours_count) { + short contour = *contours + 1; + convert_contour(points + last_contour, tags + last_contour, contour - last_contour); + last_contour = contour; + } + assert(last_contour == points_count); + + assert(segments_count <= SEGMENTS_COUNT_MAX); /* oops... we overwrote some memory */ + assert(coords_count <= COORDS_COUNT_MAX); +} + +VCOS_STATUS_T vgft_font_init(VGFT_FONT_T *font) +{ + font->ft_face = NULL; + font->vg_font = vgCreateFont(0); + if (font->vg_font == VG_INVALID_HANDLE) + { + return VCOS_ENOMEM; + } + return VCOS_SUCCESS; +} + +VCOS_STATUS_T vgft_font_load_mem(VGFT_FONT_T *font, void *mem, size_t len) +{ + if (FT_New_Memory_Face(lib, mem, len, 0, &font->ft_face)) + { + return VCOS_EINVAL; + } + return VCOS_SUCCESS; +} + +VCOS_STATUS_T vgft_font_load_file(VGFT_FONT_T *font, const char *file) +{ + if (FT_New_Face(lib, file, 0, &font->ft_face)) { + return VCOS_EINVAL; + } + return VCOS_SUCCESS; +} + +VCOS_STATUS_T vgft_font_convert_glyphs(VGFT_FONT_T *font, unsigned int char_height, unsigned int dpi_x, unsigned int dpi_y) +{ + FT_UInt glyph_index; + FT_ULong ch; + + if (FT_Set_Char_Size(font->ft_face, 0, char_height, dpi_x, dpi_y)) + { + FT_Done_Face(font->ft_face); + vgDestroyFont(font->vg_font); + return VCOS_EINVAL; + } + + ch = FT_Get_First_Char(font->ft_face, &glyph_index); + + while (ch != 0) + { + if (FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT)) { + FT_Done_Face(font->ft_face); + vgDestroyFont(font->vg_font); + return VCOS_ENOMEM; + } + + VGPath vg_path; + FT_Outline *outline = &font->ft_face->glyph->outline; + if (outline->n_contours != 0) { + vg_path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL); + assert(vg_path != VG_INVALID_HANDLE); + + convert_outline(outline->points, outline->tags, outline->contours, outline->n_contours, outline->n_points); + vgAppendPathData(vg_path, segments_count, segments, coords); + } else { + vg_path = VG_INVALID_HANDLE; + } + + VGfloat origin[] = { 0.0f, 0.0f }; + VGfloat escapement[] = { float_from_26_6(font->ft_face->glyph->advance.x), float_from_26_6(font->ft_face->glyph->advance.y) }; + vgSetGlyphToPath(font->vg_font, glyph_index, vg_path, VG_FALSE, origin, escapement); + + if (vg_path != VG_INVALID_HANDLE) { + vgDestroyPath(vg_path); + } + ch = FT_Get_Next_Char(font->ft_face, ch, &glyph_index); + } + + return VCOS_SUCCESS; +} + +void vgft_font_term(VGFT_FONT_T *font) +{ + if (font->ft_face) + FT_Done_Face(font->ft_face); + if (font->vg_font) + vgDestroyFont(font->vg_font); + memset(font, 0, sizeof(*font)); +} + + +#define CHAR_COUNT_MAX 200 +static VGuint glyph_indices[CHAR_COUNT_MAX]; +static VGfloat adjustments_x[CHAR_COUNT_MAX]; +static VGfloat adjustments_y[CHAR_COUNT_MAX]; + +// Draws the first char_count characters from text, with adjustments, starting +// from the current origin. The peek argument indicates whether to peek ahead +// and get a final adjustment based on the next character past char_count, or +// else just assume that this is the end of the text and add no final +// adjustment. +// +// Returns silently in some error cases. Assert fails in some error cases. + +static void draw_chars(VGFT_FONT_T *font, const char *text, int char_count, VGbitfield paint_modes, int peek) { + // Put in first character + glyph_indices[0] = FT_Get_Char_Index(font->ft_face, text[0]); + int prev_glyph_index = glyph_indices[0]; + + // Calculate glyph_indices and adjustments + int i; + FT_Vector kern; + for (i = 1; i != char_count; ++i) { + int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); + if (!glyph_index) { return; } + glyph_indices[i] = glyph_index; + + if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0); + adjustments_x[i - 1] = float_from_26_6(kern.x); + adjustments_y[i - 1] = float_from_26_6(kern.y); + + prev_glyph_index = glyph_index; + } + + // Get the last adjustment? + if (peek) { + int peek_glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); + if (!peek_glyph_index) { return; } + if (FT_Get_Kerning(font->ft_face, prev_glyph_index, peek_glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0); + adjustments_x[char_count - 1] = float_from_26_6(kern.x); + adjustments_y[char_count - 1] = float_from_26_6(kern.y); + } else { + adjustments_x[char_count - 1] = 0.0f; + adjustments_y[char_count - 1] = 0.0f; + } + + vgDrawGlyphs(font->vg_font, char_count, glyph_indices, adjustments_x, adjustments_y, paint_modes, VG_FALSE); +} + +// Goes to the x,y position and draws arbitrary number of characters, draws +// iteratively if the char_count exceeds the max buffer size given above. + +static void draw_line(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, int char_count, VGbitfield paint_modes) { + if (char_count == 0) return; + + // Set origin to requested x,y + VGfloat glor[] = { x, y }; + vgSetfv(VG_GLYPH_ORIGIN, 2, glor); + + // Draw the characters in blocks to reuse buffer memory + const char *curr_text = text; + int chars_left = char_count; + while (chars_left > CHAR_COUNT_MAX) { + draw_chars(font, curr_text, CHAR_COUNT_MAX, paint_modes, 1); + chars_left -= CHAR_COUNT_MAX; + curr_text += CHAR_COUNT_MAX; + } + + // Draw the last block + draw_chars(font, curr_text, chars_left, paint_modes, 0); +} + +// Draw multiple lines of text, starting from the given x and y. The x and y +// correspond to the lower left corner of the first line of text, without +// descenders. Unfortunately, for multiline text, this ends up in the middle of +// the y-extent of the block. + +void vgft_font_draw(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, unsigned text_length, VGbitfield paint_modes) +{ + VGfloat descent = float_from_26_6(font->ft_face->size->metrics.descender); + int last_draw = 0; + int i = 0; + y -= descent; + for (;;) { + int last = !text[i] || (text_length && i==text_length); + + if ((text[i] == '\n') || last) + { + draw_line(font, x, y, text + last_draw, i - last_draw, paint_modes); + last_draw = i+1; + y -= float_from_26_6(font->ft_face->size->metrics.height); + } + if (last) + { + break; + } + ++i; + } +} + +// Get text extents for a single line. Returns silently in some error cases. +// Assert fails in some error cases. + +static void line_extents(VGFT_FONT_T *font, VGfloat *x, VGfloat *y, const char *text, int chars_count) +{ + int i; + int prev_glyph_index = 0; + if (chars_count == 0) return; + + for (i=0; i < chars_count; i++) + { + int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); + if (!glyph_index) return; + + if (i != 0) + { + FT_Vector kern; + if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index, + FT_KERNING_DEFAULT, &kern)) + { + assert(0); + } + *x += float_from_26_6(kern.x); + *y += float_from_26_6(kern.y); + } + FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT); + *x += float_from_26_6(font->ft_face->glyph->advance.x); + + prev_glyph_index = glyph_index; + } +} + +// Text extents for some ASCII text. +// +// Use text_length if non-zero, otherwise look for trailing '\0'. + +void vgft_get_text_extents(VGFT_FONT_T *font, + const char *text, + unsigned text_length, + VGfloat unused0, VGfloat unused1, + VGfloat *w, VGfloat *h) { + int last_draw = 0; + VGfloat max_x = 0; + VGfloat y = 0; + + int i, last; + for (i = 0, last = 0; !last; ++i) { + last = !text[i] || (text_length && i==text_length); + if ((text[i] == '\n') || last) { + VGfloat x = 0; + line_extents(font, &x, &y, text + last_draw, i - last_draw); + last_draw = i + 1; + y -= float_from_26_6(font->ft_face->size->metrics.height); + if (x > max_x) max_x = x; + } + } + *w = max_x; + *h = -y; +} + +// Get y offset for first line; mitigates issue of start y being middle of block +// for multiline renders by vgft_font_draw. Currently simple, may be worth +// adding y kerning? + +VGfloat vgft_first_line_y_offset(VGFT_FONT_T *font) { + return float_from_26_6(font->ft_face->size->metrics.height); +} diff --git a/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.h b/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.h new file mode 100755 index 0000000..4fe548d --- /dev/null +++ b/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.h @@ -0,0 +1,70 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +#ifndef VGFT_H +#define VGFT_H + +#include "interface/vcos/vcos.h" +#include +#include + +typedef int VGFT_BOOL; +#define VGFT_FALSE 0 +#define VGFT_TRUE (!VGFT_FALSE) + +#include FT_FREETYPE_H + +/* Returns 0 on success */ +extern int vgft_init(void); +extern void vgft_term(void); + +typedef struct { + VGFont vg_font; + FT_Face ft_face; +} VGFT_FONT_T; + +/** Initialise a FT->VG font */ +VCOS_STATUS_T vgft_font_init(VGFT_FONT_T *font); + +/** Load a font file from memory */ +VCOS_STATUS_T vgft_font_load_mem(VGFT_FONT_T *font, void *mem, size_t len); + +/** Convert a font into VG glyphs */ +VCOS_STATUS_T vgft_font_convert_glyphs(VGFT_FONT_T *font, unsigned int char_height, unsigned int dpi_x, unsigned int dpi_y); + +/** Release a font. */ +void vgft_font_term(VGFT_FONT_T *font); + +void vgft_font_draw(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, unsigned text_length, VGbitfield paint_modes); + +void vgft_get_text_extents(VGFT_FONT_T *font, const char *text, unsigned text_length, VGfloat start_x, VGfloat start_y, VGfloat *w, VGfloat *h); + +VGfloat vgft_first_line_y_offset(VGFT_FONT_T *font); + +#endif diff --git a/host_applications/linux/apps/hello_pi/rebuild.sh b/host_applications/linux/apps/hello_pi/rebuild.sh new file mode 100755 index 0000000..8225dd5 --- /dev/null +++ b/host_applications/linux/apps/hello_pi/rebuild.sh @@ -0,0 +1,34 @@ +make -C libs/ilclient clean +make -C libs/vgfont clean +make -C hello_world clean +make -C hello_triangle clean +make -C hello_triangle2 clean +make -C hello_video clean +make -C hello_audio clean +make -C hello_font clean +make -C hello_dispmanx clean +make -C hello_tiger clean +make -C hello_encode clean +make -C hello_jpeg clean +make -C hello_videocube clean +make -C hello_teapot clean +make -C hello_fft clean +make -C hello_mmal_encode clean + +make -C libs/ilclient +make -C libs/vgfont +make -C hello_world +make -C hello_triangle +make -C hello_triangle2 +make -C hello_video +make -C hello_audio +make -C hello_font +make -C hello_dispmanx +make -C hello_tiger +make -C hello_encode +make -C hello_jpeg +make -C hello_videocube +make -C hello_teapot +make -C hello_fft +make -C hello_mmal_encode + diff --git a/host_applications/linux/apps/raspicam/CMakeLists.txt b/host_applications/linux/apps/raspicam/CMakeLists.txt new file mode 100755 index 0000000..e6aa6b8 --- /dev/null +++ b/host_applications/linux/apps/raspicam/CMakeLists.txt @@ -0,0 +1,36 @@ + +# raspistill/raspivid/raspiyuv + +SET(COMPILE_DEFINITIONS -Werror) + +include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/libs/bcm_host/include) +include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/apps/raspicam/) +include_directories(${PROJECT_SOURCE_DIR}/host_applications/linux/libs/sm) + +set (GL_SCENE_SOURCES + gl_scenes/models.c + gl_scenes/mirror.c + gl_scenes/yuv.c + gl_scenes/sobel.c + gl_scenes/square.c + gl_scenes/teapot.c + gl_scenes/vcsm_square.c) + +set (COMMON_SOURCES + RaspiCamControl.c + RaspiCLI.c + RaspiPreview.c) + +add_executable(raspistill ${COMMON_SOURCES} RaspiStill.c RaspiTex.c RaspiTexUtil.c tga.c ${GL_SCENE_SOURCES}) +add_executable(raspiyuv ${COMMON_SOURCES} RaspiStillYUV.c) +add_executable(raspivid ${COMMON_SOURCES} RaspiVid.c) +add_executable(raspividyuv ${COMMON_SOURCES} RaspiVidYUV.c) + +set (MMAL_LIBS mmal_core mmal_util mmal_vc_client) + +target_link_libraries(raspistill ${MMAL_LIBS} vcos bcm_host brcmGLESv2 brcmEGL m) +target_link_libraries(raspiyuv ${MMAL_LIBS} vcos bcm_host) +target_link_libraries(raspivid ${MMAL_LIBS} vcos bcm_host) +target_link_libraries(raspividyuv ${MMAL_LIBS} vcos bcm_host) + +install(TARGETS raspistill raspiyuv raspivid raspividyuv RUNTIME DESTINATION bin) diff --git a/host_applications/linux/apps/raspicam/Doxyfile b/host_applications/linux/apps/raspicam/Doxyfile new file mode 100755 index 0000000..94285b9 --- /dev/null +++ b/host_applications/linux/apps/raspicam/Doxyfile @@ -0,0 +1,1869 @@ +# Doxyfile 1.8.3.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "RaspiCam" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 1 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = Raspberry Pi Camera support applications + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page (index.html). +# This can be useful if you have a project on for instance GitHub and want reuse +# the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search engine +# library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# manageable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/host_applications/linux/apps/raspicam/Makefile b/host_applications/linux/apps/raspicam/Makefile new file mode 100755 index 0000000..ca1853c --- /dev/null +++ b/host_applications/linux/apps/raspicam/Makefile @@ -0,0 +1,6 @@ +OBJS=RaspiCamControl.o RaspiCLI.o RaspiPreview.o RaspiStill.o +BIN=raspicam.bin +LDFLAGS+=-lmmal -lmmal_core -lmmal_util + +include ../Makefile.include + diff --git a/host_applications/linux/apps/raspicam/README.md b/host_applications/linux/apps/raspicam/README.md new file mode 100755 index 0000000..6232cab --- /dev/null +++ b/host_applications/linux/apps/raspicam/README.md @@ -0,0 +1,477 @@ +RaspiCam Documentation + +This document describes the use of the three Raspberry Pi camera applications as of October 11th 2013. + +There are three applications provided, raspistill, raspivid and raspistillyuv. raspistill and raspistillyuv are very similar and are intended for capturing images, raspivid is for capturing video. + +All the applications are command line driven, written to take advantage of the mmal API which runs over OpenMAX. The mmal API provides an easier to use system than that presented by OpenMAX. Note that mmal is a Broadcom specific API used only on Videocore 4 systems. + +The applications use up to four OpenMAX(mmal) components - camera, preview, encoder and null_sink. All applications use the camera component, raspistill uses the Image Encode component, raspivid uses the Video Encode component and raspistillyuv does not use an encoder, and sends its YUV or RGB output direct from camera component to file. + +The preview display is optional, but can be used full screen or directed to a specific rectangular area on the display. If preview is disabled, the null_sink component is used to 'absorb' the preview frames. It is necessary for the camera to produce preview frames even if not required for display, as they are used for calculating exposure and white balance settings. + +In addition it is possible to omit the filename option, in which case the preview is displayed but no file is written, or to redirect all output to stdout. + +Command line help is available by typing just the application name in on the command line. + + +Setting up the Camera hardware + + +Warning. Cameras are static sensitive. Earth yourself prior to handling the PCB, a sink tap/faucet or similar should suffice if you don't have an earthing strap. +The camera board attaches to the Raspberry Pi via a 15 way ribbon cable. There are only two connections to make, the ribbon cable need to be attached to the camera PCB and the Raspberry Pi itself. You need to get it the right way round or the camera will not work. On the camera PCB, the blue backing on the cable should be away from the PCB, and on the Raspberry Pi it should be towards the Ethernet connection (or where the Ethernet connector would be if you are using a model A). + +Although the connectors on the PCB and the Pi are different, they work in a similar way. On the Raspberry Pi, pull up the tabs on each end of the connector. It should slide up easily, and be able to pivot around slightly. Fully insert the ribbon cable into the slot, ensuring it is straight, then gently press down the tabs to clip it into place. The camera PCB itself also requires you to pull the tabs away from the board, gently insert the cable, then push the tabs back. The PCB connector is a little more awkward than the one on the Pi itself. + +Setting up the Camera software + +Execute the following instructions on the command line to download and install the latest kernel, GPU firmware and applications. You will need a internet connection for this to work correctly. + +sudo apt-get update +sudo apt-get upgrade + +Now you need to enable camera support using the raspi-config program you will have used when you first set up your Raspberry Pi. + +sudo raspi-config + +Use the cursor keys to move to the camera option and select enable. On exiting raspi-config it will ask to reboot. The enable option will ensure that on reboot the correct GPU firmware will be running (with the camera driver and tuning), and the GPU memory split is sufficient to allow the camera to acquire enough memory to run correctly. + +To test that the system is installed and working, try the following command : + +raspistill -v -o test.jpg + +The display should show a 5 second preview from the camera and then take a picture, saved to the file test.jpg, whilst display various informational messages. + +Troubleshooting + +If the camera is not working correctly, there are number of things to try. +Are the ribbon connectors all firmly seated and the right way round? They must be straight in their sockets. +Is the camera module connector firmly attached to the camera PCB? This is the connection from the smaller black camera module itself to the camera PCB. Sometimes this connection can come loose. Using a fingernail, flip up the connector on the PCB, then reseat it with gentle pressure, it engages with a very slight click. +Have sudo apt-get update, sudo apt-get upgrade been run? +Has raspi-config been run and the camera enabled? +Is your power supply sufficient? The camera adds about 200-250mA to the power requirements of the Raspberry Pi. + +So, if things are still not working, try the following: + +Error : raspistill/raspivid not found. This probably means your update/upgrade failed in some way. Try it again. + +Error : ENOMEM displayed. Camera is not starting up. Check all connections again. + +Error : ENOSPC displayed. Camera is probably running out of GPU memory. Check config.txt in the /boot/ folder. The gpu_mem option should be at least 128. + +If after all the above, the camera is still not working, it may be defective. Try posting on the Raspberry Pi forum (Camera section) to see if there is any more help available there. + +Common Command line Options +Preview Window + + + --preview, -p Preview window settings <'x,y,w,h'> + +Allows the user to define the size and location on the screen that the preview window will be placed. Note this will be superimposed over the top of any other windows/graphics. + + --fullscreen, -f Fullscreen preview mode + +Forces the preview window to use the whole screen. Note that the aspect ratio of the incoming image will be retained, so there may be bars on some edges. + + --nopreview, -n, Do not display a preview window + +Disables the preview window completely. Note that even though the preview is disabled, the camera will still be producing frames, so will be using power. + + --opacity, -op Set preview window opacity + +Sets the opacity of the preview windows. 0 = invisible, 255 = fully opaque. + +Camera Control Options + + + --sharpness, -sh Set image sharpness (-100 to 100) + +Set the sharpness of the image, 0 is the default. + + --contrast, -co Set image contrast (-100 to 100) + +Set the contrast of the image, 0 is the default + + --brightness, -br Set image brightness (0 to 100) + +Set the brightness of the image, 50 is the default. 0 is black, 100 is white. + + --saturation, -sa Set image saturation (-100 to 100) + +set the colour saturation of the image. 0 is the default. + + --ISO, -ISO Set capture ISO + +Sets the ISO to be used for captures. Range is 100 to 800. + --vstab, -vs Turn on video stabilisation + +In video mode only, turn on video stabilisation. + + --ev, -ev Set EV compensation + +Set the EV compensation of the image. Range is -10 to +10, default is 0. + + --exposure, -ex Set exposure mode + +Possible options are: + +auto Use automatic exposure mode +night Select setting for night shooting +nightpreview +backlight Select setting for back lit subject +spotlight +sports Select setting for sports (fast shutter etc) +snow Select setting optimised for snowy scenery +beach Select setting optimised for beach +verylong Select setting for long exposures +fixedfps, Constrain fps to a fixed value +antishake Antishake mode +fireworks Select settings + +Note that not all of these settings may be implemented, depending on camera tuning. + + --awb, -awb Set Automatic White Balance (AWB) mode + +off Turn off white balance calculation +auto Automatic mode (default) +sun Sunny mode +cloud Cloudy mode +shade Shaded mode +tungsten Tungsten lighting mode +fluorescent Fluorescent lighting mode +incandescent Incandescent lighting mode +flash Flash mode +horizon Horizon mode + + --imxfx, -ifx Set image effect + +Set an effect to be applied to the image + +none NO effect (default) +negative Negate the image +solarise Solarise the image +posterize Posterise the image +whiteboard Whiteboard effect +blackboard Blackboard effect +sketch Sketch style effect +denoise Denoise the image +emboss Emboss the image +oilpaint Apply an oil paint style effect +hatch Hatch sketch style +gpen +pastel A pastel style effect +watercolour A watercolour style effect +film Film grain style effect +blur Blur the image +saturation Colour saturate the image +colourswap Not fully implemented +washedout Not fully implemented +posterise Not fully implemented +colourpoint Not fully implemented +colourbalance Not fully implemented +cartoon Not fully implemented + + --colfx, -cfx Set colour effect + +The supplied U and V parameters (range 0 to 255) are applied to the U and Y channels of the image. For example, --colfx 128:128 should result in a monochrome image. + + --metering, -mm Set metering mode + +Specify the metering mode used for the preview and capture + +average Average the whole frame for metering. +spot Spot metering +backlit Assume a backlit image +matrix Matrix metering + + --rotation, -rot Set image rotation (0-359) + +Sets the rotation of the image in viewfinder and resulting image. This can take any value from 0 upwards, but due to hardware constraints only 0, 90, 180 and 270 degree rotations are supported. + + + --hflip, -hf Set horizontal flip + +Flips the preview and saved image horizontally. + + --vflip, -vf Set vertical flip + +Flips the preview and saved image vertically. + + --roi, -roi Set sensor region of interest + +Allows the specification of the area of the sensor to be used as the source for the preview and capture. This is defined as x,y for the top left corner, and a width and height, all values in normalised coordinates (0.0-1.0). So to set a ROI at half way across and down the sensor, and an width and height of a quarter of the sensor use : + + -roi 0.5,0.5,0.25,0.25 + + --shutter, -ss Set shutter speed + +Set the shutter speed to the specified value (in microseconds). There is currently an upper limit of approximately 330000us (330ms, 0.33s) past which operation is undefined. This is being investigated. + + --camselect, -cs Select + +Select camera number. Default 0 + +Application specific settings + +raspistill + +--width, -w Set image width +--height, -h Set image height +--quality, -q Set jpeg quality <0 to 100> + +Quality 100 is almost completely uncompressed. 75 is a good all round value + + --raw, -r Add raw bayer data to jpeg metadata + +This option inserts the raw Bayer data from the camera in to the JPEG metadata + + --output -o Output filename . + +Specify the output filename. If not specified, no file is saved. If the filename is '-', then all output is sent to stdout. + + --latest -l Link latest frame to filename + +Make a file system link under this name to the latest frame. + + --verbose, -v Output verbose information during run + +Outputs debugging/information messages during the program run. + + --timeout, -t Time before takes picture and shuts down. + +The program will run for this length of time, then take the capture (if output is specified). If not specified, this is set to 5 seconds. + + --timelapse, -tl Timelapse mode. + +The specific value is the time between shots in milliseconds. Note you should specify %04d at the point in the filename where you want a frame count number to appear. e.g. + + -t 30000 -tl 2000 -o image%04d.jpg + +You can also name the files with timestamp (-ts) or datetime(-dt) + + -t 30000 -tl 2000 -ts -o image%d.jpg + -t 30000 -tl 2000 -dt -o image2015%10d.jpg + +will produce a capture every 2 seconds, over a total period of 30s, named image1.jpg, image0002.jpg..image0015.jpg. Note that the %04d indicates a 4 digit number with leading zero's added to pad to the required number of digits. So, for example, %08d would result in an 8 digit number. + +If a timelapse value of 0 is entered, the application will take pictures as fast as possible. Note there is an minimum enforced pause of 30ms between captures to ensure that exposure calculations can be made. + + --thumb, -th Set thumbnail parameters (x:y:quality) + +Allows specification of the thumbnail image inserted in to the JPEG file. If not specified, defaults are a size of 64x48 at quality 35. + + --demo, -d Run a demo mode + +This options cycles through range of camera options, no capture is done, the demo will end at the end of the timeout period, irrespective of whether all the options have been cycled. The time between cycles should be specified as a millisecond value. + + --encoding, -e Encoding to use for output file + +Valid options are jpg, bmp, gif and png. Note that unaccelerated image types (gif, png, bmp) will take much longer to save than JPG which is hardware accelerated. Also note that the filename suffix is completely ignored when deciding the encoding of a file. + + --exif, -x EXIF tag to apply to captures (format as 'key=value') + +Allows the insertion of specific exif tags in to the JPEG image. You can have up to 32 exif tge entries. This is useful for things like adding GPS metadata. For example, to set the Longitude + + --exif GPS.GPSLongitude=5/1,10/1,15/100 + +would set the Longitude to 5degs, 10 minutes, 15 seconds. See exif documentation for more details on the range of tags available; the supported tags are as follows. + +IFD0.< or +IFD1.< +ImageWidth, ImageLength, BitsPerSample, Compression, PhotometricInterpretation, ImageDescription, Make, Model, StripOffsets, Orientation, SamplesPerPixel, RowsPerString, StripByteCounts, Xresolution, Yresolution, PlanarConfiguration, ResolutionUnit, TransferFunction, Software, DateTime, Artist, WhitePoint, PrimaryChromaticities, JPEGInterchangeFormat, JPEGInterchangeFormatLength, YcbCrCoefficients, YcbCrSubSampling, YcbCrPositioning, ReferenceBlackWhite, Copyright> + +EXIF.< +ExposureTime, FNumber, ExposureProgram, SpectralSensitivity, a +ISOSpeedRatings, OECF, ExifVersion, DateTimeOriginal, DateTimeDigitized, ComponentsConfiguration, CompressedBitsPerPixel, ShutterSpeedValue, ApertureValue, BrightnessValue, ExposureBiasValue, MaxApertureValue, SubjectDistance, MeteringMode, LightSource, Flash, FocalLength, SubjectArea, MakerNote, UserComment, SubSecTime, SubSecTimeOriginal, SubSecTimeDigitized, FlashpixVersion, ColorSpace, PixelXDimension, PixelYDimension, RelatedSoundFile, FlashEnergy, SpacialFrequencyResponse, FocalPlaneXResolution, FocalPlaneYResolution, FocalPlaneResolutionUnit, SubjectLocation, ExposureIndex, SensingMethod, FileSource, SceneType, CFAPattern, CustomRendered, ExposureMode, +WhiteBalance, DigitalZoomRatio, FocalLengthIn35mmFilm, SceneCaptureType, GainControl, Contrast, Saturation, Sharpness, DeviceSettingDescription, SubjectDistanceRange, ImageUniqueID> + +GPS.< +GPSVersionID, GPSLatitudeRef, GPSLatitude, GPSLongitudeRef, GPSLongitude, GPSAltitudeRef, GPSAltitude, GPSTimeStamp, GPSSatellites, GPSStatus, GPSMeasureMode, GPSDOP, GPSSpeedRef, GPSSpeed, GPSTrackRef, GPSTrack, GPSImgDirectionRef, GPSImgDirection, GPSMapDatum, GPSDestLatitudeRef, GPSDestLatitude, GPSDestLongitudeRef, GPSDestLongitude, GPSDestBearingRef, GPSDestBearing, GPSDestDistanceRef, GPSDestDistance, GPSProcessingMethod, GPSAreaInformation, GPSDateStamp, GPSDifferential> + +EINT.< +InteroperabilityIndex, InteroperabilityVersion, RelatedImageFileFormat, RelatedImageWidth, RelatedImageLength> + +Note that a small subset of these tags will be set automatically by the camera system, but will be overridden by any exif options on the command line. + + --fullpreview, -fp Full Preview mode + +This runs the preview windows using the full resolution capture mode. Maximum frames per second in this mode is 15fps and the preview will have the same field of view as the capture. Captures should happen more quickly as no mode change should be required. This feature is currently under development. + + --keypress -k Keypress mode + +The camera is run for the requested time (-t), and a captures can be initiated throughout that by pressing the Enter key. Press X then Enter will exit the application before the timeout is reached. If the timeout is set to 0, the camera will run indefinitely until X then Enter is typed. +Additional supported keys: 'i', 'o' and 'r'. Press 'i' then Enter will zoom in by 10%. Press 'o' then Enter will zoom out by 10%. Press 'r' then Enter will reset zoom. +Using the verbose option (-v) will display a prompt asking for user input, otherwise no prompt is displayed. + + --signal -s Signal mode + +The camera is run for the requested time (-t), and a captures can be initiated throughout that time by sending a USR1 signal to the camera process. If USR2 signal is received the application makes a capture and exits. This can be done using the kill command. You can find the camera process ID using the 'ps ax' command or using pidof command. + + kill -USR1 + kill -USR2 + +raspistillyuv + + +Many of the options for raspistillyuv are the same as those for raspistill. This section shows the differences. + +Unsupported Options: + --exif, --encoding, --thumb, --raw, --quality + +Extra Options : + + --rgb, -rgb Save uncompressed data as RGB888 + +This option forces the image to be saved as RGB data with 8 bits per channel, rather than YUV420. + +Note that the image buffers saved in raspistillyuv are padded to a horizontal size divisible by 16 (so there may be unused bytes at the end of each line to made the width divisible by 16). Buffers are also padded vertically to be divisible by 16, and in the YUV mode, each plane of Y,U,V is padded in this way. + + +raspivid + + + --width, -w Set image width + +Width of resulting video. This should be between 64 and 1920. + + --height, -h Set image height + +Height of resulting video. This should be between 64 and 1080. + + --bitrate, -b Set bitrate. + +Use bits per second, so 10MBits/s would be -b 10000000. For H264, 1080p a high quality bitrate would be 15Mbits/s or more. + + --output -o Output filename . + +Specify the output filename. If not specified, no file is saved. If the filename is '-', then all output is sent to stdout. + + --verbose, -v Output verbose information during run + +Outputs debugging/information messages during the program run. + + --timeout, -t Time before takes picture and shuts down. + +The program will run for this length of time, then take the capture (if output is specified). If not specified, this is set to 5seconds. Setting 0 will mean the application will run continuously until stopped with Ctrl-C. + + --demo, -d Run a demo mode + +This options cycles through range of camera options, no capture is done, the demo will end at the end of the timeout period, irrespective of whether all the options have been cycled. The time between cycles should be specified as a millisecond value. + + --framerate, -fps Specify the frames per second to record + +At present, the minimum frame rate allowed is 2fps, the maximum is 30fps. This is likely to change in the future. + + --penc, -e Display preview image *after* encoding + +Switch on an option to display the preview after compression. This will show any compression artefacts in the preview window. In normal operation, the preview will show the camera output prior to being compressed. This option is not guaranteed to work in future releases. + + --intra, -g Specify the intra refresh period (key frame rate/GoP) + +Sets the intra refresh period (GoP) rate for the recorded video. H264 video uses a complete frame (I-frame) every intra refresh period from which subsequent frames are based. This options specifies the numbers of frames between each I-frame. Larger numbers here will reduce the size of the resulting video, smaller numbers make the stream more robust to error. Setting 0 will produce an initial I-frame and then just P-frames. + + --profile, -pf Specify H264 profile to use for encoding + +Sets the H264 profile to be used for the encoding. Options are : baseline, main, high. + +Examples + +Still captures + +By default, captures are done at the highest resolution supported by the sensor. This can be changed using the -w and -h command line options. + +Taking a default capture after 2s (note times are specified in milliseconds) on viewfinder, saving in image.jpg + + raspistill -t 2000 -o image.jpg + +Take a capture at a different resolution. + + raspistill -t 2000 -o image.jpg -w 640 -h 480 + +Now reduce the quality considerably to reduce file size + + raspistill -t 2000 -o image.jpg -q 5 + +Force the preview to appear at coordinate 100,100, with width 300 and height 200 pixels. + + raspistill -t 2000 -o image.jpg -p 100,100,300,200 + +Disable preview entirely + + raspistill -t 2000 -o image.jpg -n + +Save the image as a png file (lossless compression, but slower than JPEG). Note that the filename suffix is ignored when choosing the image encoding. + + raspistill -t 2000 -o image.png -e png + +Add some EXIF information to the JPEG. This sets the Artist tag name to Boris, and the GPS altitude to 123.5m. Note that if setting GPS tags you should set as a minimum GPSLatitude, GPSLatitudeRef, GPSLongitude, GPSLongitudeRef, GPSAltitude and GPSAltitudeRef. + + raspistill -t 2000 -o image.jpg -x IFDO.Artist=Boris -x GPS.GPSAltitude=1235/10 + +Set an emboss style image effect + + raspistill -t 2000 -o image.jpg -ifx emboss + +Set the U and V channels of the YUV image to specific values (128:128 produces a greyscale image) + + raspistill -t 2000 -o image.jpg -cfx 128:128 + +Run preview ONLY for 2s, no saved image. + + raspistill -t 2000 + +Take timelapse picture, one every 10 seconds for 10 minutes (10 minutes = 600000ms), named image_num_001_today.jpg, image_num_002_today.jpg onwards, with the latest picture also available under the name latest.jpg. + + raspistill -t 600000 -tl 10000 -o image_num_%03d_today.jpg -l latest.jpg + +Take a picture and send image data to stdout + + raspistill -t 2000 -o - + +Take a picture and send image data to file + + raspistill -t 2000 -o - > my_file.jpg + +Run camera forever, taking a picture when Enter is pressed + + raspistill -t 0 -k -o my_pics%02d.jpg +Video Captures + +Image size and preview settings are the same as for stills capture. Default size for video recording is 1080p (1920x1080) + +Record a 5s clip with default settings (1080p30) + + raspivid -t 5000 -o video.h264 + +Record a 5s clip at a specified bitrate (3.5MBits/s) + + raspivid -t 5000 -o video.h264 -b 3500000 + +Record a 5s clip at a specified framerate (5fps) + + raspivid -t 5000 -o video.h264 -f 5 + +Encode a 5s camera stream and send image data to stdout + + raspivid -t 5000 -o - + +Encode a 5s camera stream and send image data to file + + raspivid -t 5000 -o - > my_file.h264 + + + +Shell Error Codes + +The applications described here will return a standard error code to the shell on completion. Possible error codes are : + +ER_OK 0 Application ran successfully. +EX_USAGE 64 Bad command line parameter +ER_SOFTWARE 70 Software or camera error + 130 Application terminated by ctrl-C. + + + + + + diff --git a/host_applications/linux/apps/raspicam/RaspiCLI.c b/host_applications/linux/apps/raspicam/RaspiCLI.c new file mode 100755 index 0000000..9f858f4 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiCLI.c @@ -0,0 +1,155 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiCLI.c + * Code for handling command line parameters + * + * \date 4th March 2013 + * \Author: James Hughes + * + * Description + * + * Some functions/structures for command line parameter parsing + * + */ +#include +#include +#include +#include + +#include "interface/vcos/vcos.h" + +#include "RaspiCLI.h" + + +/** + * Convert a string from command line to a comand_id from the list + * + * @param commands Array of command to check + * @param num_command Number of commands in the array + * @param arg String to search for in the list + * @param num_parameters Returns the number of parameters used by the command + * + * @return command ID if found, -1 if not found + * + */ +int raspicli_get_command_id(const COMMAND_LIST *commands, const int num_commands, const char *arg, int *num_parameters) +{ + int command_id = -1; + int j; + + vcos_assert(commands); + vcos_assert(num_parameters); + vcos_assert(arg); + + if (!commands || !num_parameters || !arg) + return -1; + + for (j = 0; j < num_commands; j++) + { + if (!strcmp(arg, commands[j].command) || + !strcmp(arg, commands[j].abbrev)) + { + // match + command_id = commands[j].id; + *num_parameters = commands[j].num_parameters; + break; + } + } + + return command_id; +} + + +/** + * Display the list of commands in help format + * + * @param commands Array of command to check + * @param num_command Number of commands in the arry + * + * + */ +void raspicli_display_help(const COMMAND_LIST *commands, const int num_commands) +{ + int i; + + vcos_assert(commands); + + if (!commands) + return; + + for (i = 0; i < num_commands; i++) + { + fprintf(stdout, "-%s, -%s\t: %s\n", commands[i].abbrev, + commands[i].command, commands[i].help); + } +} + + +/** + * Function to take a string, a mapping, and return the int equivalent + * @param str Incoming string to match + * @param map Mapping data + * @param num_refs The number of items in the mapping data + * @return The integer match for the string, or -1 if no match + */ +int raspicli_map_xref(const char *str, const XREF_T *map, int num_refs) +{ + int i; + + for (i=0;i +#include +#include + +#include "interface/vcos/vcos.h" + +#include "interface/vmcs_host/vc_vchi_gencmd.h" +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "RaspiCamControl.h" +#include "RaspiCLI.h" + +/// Structure to cross reference exposure strings against the MMAL parameter equivalent +static XREF_T exposure_map[] = +{ + {"off", MMAL_PARAM_EXPOSUREMODE_OFF}, + {"auto", MMAL_PARAM_EXPOSUREMODE_AUTO}, + {"night", MMAL_PARAM_EXPOSUREMODE_NIGHT}, + {"nightpreview", MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW}, + {"backlight", MMAL_PARAM_EXPOSUREMODE_BACKLIGHT}, + {"spotlight", MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT}, + {"sports", MMAL_PARAM_EXPOSUREMODE_SPORTS}, + {"snow", MMAL_PARAM_EXPOSUREMODE_SNOW}, + {"beach", MMAL_PARAM_EXPOSUREMODE_BEACH}, + {"verylong", MMAL_PARAM_EXPOSUREMODE_VERYLONG}, + {"fixedfps", MMAL_PARAM_EXPOSUREMODE_FIXEDFPS}, + {"antishake", MMAL_PARAM_EXPOSUREMODE_ANTISHAKE}, + {"fireworks", MMAL_PARAM_EXPOSUREMODE_FIREWORKS} +}; + +static const int exposure_map_size = sizeof(exposure_map) / sizeof(exposure_map[0]); + +/// Structure to cross reference flicker avoid strings against the MMAL parameter equivalent + +static XREF_T flicker_avoid_map[] = +{ + {"off", MMAL_PARAM_FLICKERAVOID_OFF}, + {"auto", MMAL_PARAM_FLICKERAVOID_AUTO}, + {"50hz", MMAL_PARAM_FLICKERAVOID_50HZ}, + {"60hz", MMAL_PARAM_FLICKERAVOID_60HZ} +}; + +static const int flicker_avoid_map_size = sizeof(flicker_avoid_map) / sizeof(flicker_avoid_map[0]); + +/// Structure to cross reference awb strings against the MMAL parameter equivalent +static XREF_T awb_map[] = +{ + {"off", MMAL_PARAM_AWBMODE_OFF}, + {"auto", MMAL_PARAM_AWBMODE_AUTO}, + {"sun", MMAL_PARAM_AWBMODE_SUNLIGHT}, + {"cloud", MMAL_PARAM_AWBMODE_CLOUDY}, + {"shade", MMAL_PARAM_AWBMODE_SHADE}, + {"tungsten", MMAL_PARAM_AWBMODE_TUNGSTEN}, + {"fluorescent", MMAL_PARAM_AWBMODE_FLUORESCENT}, + {"incandescent", MMAL_PARAM_AWBMODE_INCANDESCENT}, + {"flash", MMAL_PARAM_AWBMODE_FLASH}, + {"horizon", MMAL_PARAM_AWBMODE_HORIZON} +}; + +static const int awb_map_size = sizeof(awb_map) / sizeof(awb_map[0]); + +/// Structure to cross reference image effect against the MMAL parameter equivalent +static XREF_T imagefx_map[] = +{ + {"none", MMAL_PARAM_IMAGEFX_NONE}, + {"negative", MMAL_PARAM_IMAGEFX_NEGATIVE}, + {"solarise", MMAL_PARAM_IMAGEFX_SOLARIZE}, + {"sketch", MMAL_PARAM_IMAGEFX_SKETCH}, + {"denoise", MMAL_PARAM_IMAGEFX_DENOISE}, + {"emboss", MMAL_PARAM_IMAGEFX_EMBOSS}, + {"oilpaint", MMAL_PARAM_IMAGEFX_OILPAINT}, + {"hatch", MMAL_PARAM_IMAGEFX_HATCH}, + {"gpen", MMAL_PARAM_IMAGEFX_GPEN}, + {"pastel", MMAL_PARAM_IMAGEFX_PASTEL}, + {"watercolour", MMAL_PARAM_IMAGEFX_WATERCOLOUR}, + {"film", MMAL_PARAM_IMAGEFX_FILM}, + {"blur", MMAL_PARAM_IMAGEFX_BLUR}, + {"saturation", MMAL_PARAM_IMAGEFX_SATURATION}, + {"colourswap", MMAL_PARAM_IMAGEFX_COLOURSWAP}, + {"washedout", MMAL_PARAM_IMAGEFX_WASHEDOUT}, + {"posterise", MMAL_PARAM_IMAGEFX_POSTERISE}, + {"colourpoint", MMAL_PARAM_IMAGEFX_COLOURPOINT}, + {"colourbalance", MMAL_PARAM_IMAGEFX_COLOURBALANCE}, + {"cartoon", MMAL_PARAM_IMAGEFX_CARTOON} + }; + +static const int imagefx_map_size = sizeof(imagefx_map) / sizeof(imagefx_map[0]); + +static XREF_T metering_mode_map[] = +{ + {"average", MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE}, + {"spot", MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT}, + {"backlit", MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT}, + {"matrix", MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX} +}; + +static const int metering_mode_map_size = sizeof(metering_mode_map)/sizeof(metering_mode_map[0]); + +static XREF_T drc_mode_map[] = +{ + {"off", MMAL_PARAMETER_DRC_STRENGTH_OFF}, + {"low", MMAL_PARAMETER_DRC_STRENGTH_LOW}, + {"med", MMAL_PARAMETER_DRC_STRENGTH_MEDIUM}, + {"high", MMAL_PARAMETER_DRC_STRENGTH_HIGH} +}; + +static const int drc_mode_map_size = sizeof(drc_mode_map)/sizeof(drc_mode_map[0]); + +static XREF_T stereo_mode_map[] = +{ + {"off", MMAL_STEREOSCOPIC_MODE_NONE}, + {"sbs", MMAL_STEREOSCOPIC_MODE_SIDE_BY_SIDE}, + {"tb", MMAL_STEREOSCOPIC_MODE_TOP_BOTTOM}, +}; + +static const int stereo_mode_map_size = sizeof(stereo_mode_map)/sizeof(stereo_mode_map[0]); + + +#define CommandSharpness 0 +#define CommandContrast 1 +#define CommandBrightness 2 +#define CommandSaturation 3 +#define CommandISO 4 +#define CommandVideoStab 5 +#define CommandEVComp 6 +#define CommandExposure 7 +#define CommandAWB 8 +#define CommandImageFX 9 +#define CommandColourFX 10 +#define CommandMeterMode 11 +#define CommandRotation 12 +#define CommandHFlip 13 +#define CommandVFlip 14 +#define CommandROI 15 +#define CommandShutterSpeed 16 +#define CommandAwbGains 17 +#define CommandDRCLevel 18 +#define CommandStatsPass 19 +#define CommandAnnotate 20 +#define CommandStereoMode 21 +#define CommandStereoDecimate 22 +#define CommandStereoSwap 23 +#define CommandAnnotateExtras 24 +#define CommandFlicker 25 +#define CommandAnalogGain 26 +#define CommandDigitalGain 27 + +static COMMAND_LIST cmdline_commands[] = +{ + {CommandSharpness, "-sharpness", "sh", "Set image sharpness (-100 to 100)", 1}, + {CommandContrast, "-contrast", "co", "Set image contrast (-100 to 100)", 1}, + {CommandBrightness, "-brightness","br", "Set image brightness (0 to 100)", 1}, + {CommandSaturation, "-saturation","sa", "Set image saturation (-100 to 100)", 1}, + {CommandISO, "-ISO", "ISO","Set capture ISO", 1}, + {CommandVideoStab, "-vstab", "vs", "Turn on video stabilisation", 0}, + {CommandEVComp, "-ev", "ev", "Set EV compensation - steps of 1/6 stop", 1}, + {CommandExposure, "-exposure", "ex", "Set exposure mode (see Notes)", 1}, + {CommandFlicker, "-flicker", "fli","Set flicker avoid mode (see Notes)", 1}, + {CommandAWB, "-awb", "awb","Set AWB mode (see Notes)", 1}, + {CommandImageFX, "-imxfx", "ifx","Set image effect (see Notes)", 1}, + {CommandColourFX, "-colfx", "cfx","Set colour effect (U:V)", 1}, + {CommandMeterMode, "-metering", "mm", "Set metering mode (see Notes)", 1}, + {CommandRotation, "-rotation", "rot","Set image rotation (0-359)", 1}, + {CommandHFlip, "-hflip", "hf", "Set horizontal flip", 0}, + {CommandVFlip, "-vflip", "vf", "Set vertical flip", 0}, + {CommandROI, "-roi", "roi","Set region of interest (x,y,w,d as normalised coordinates [0.0-1.0])", 1}, + {CommandShutterSpeed,"-shutter", "ss", "Set shutter speed in microseconds", 1}, + {CommandAwbGains, "-awbgains", "awbg", "Set AWB gains - AWB mode must be off", 1}, + {CommandDRCLevel, "-drc", "drc", "Set DRC Level (see Notes)", 1}, + {CommandStatsPass, "-stats", "st", "Force recomputation of statistics on stills capture pass"}, + {CommandAnnotate, "-annotate", "a", "Enable/Set annotate flags or text", 1}, + {CommandStereoMode, "-stereo", "3d", "Select stereoscopic mode", 1}, + {CommandStereoDecimate,"-decimate","dec", "Half width/height of stereo image"}, + {CommandStereoSwap, "-3dswap", "3dswap", "Swap camera order for stereoscopic"}, + {CommandAnnotateExtras,"-annotateex","ae", "Set extra annotation parameters (text size, text colour(hex YUV), bg colour(hex YUV))", 2}, + {CommandAnalogGain, "-analoggain", "ag", "Set the analog gain (floating point)", 1}, + {CommandDigitalGain, "-digitalgain", "dg", "Set the digtial gain (floating point)", 1}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + + +#define parameter_reset -99999 + +#define zoom_full_16P16 ((unsigned int)(65536 * 0.15)) +#define zoom_increment_16P16 (65536UL / 10) + +/** + * Update the passed in parameter according to the rest of the parameters + * passed in. + * + * + * @return 0 if reached end of cycle for this parameter, !0 otherwise + */ +static int update_cycle_parameter(int *option, int min, int max, int increment) +{ + vcos_assert(option); + if (!option) + return 0; + + if (*option == parameter_reset) + *option = min - increment; + + *option += increment; + + if (*option > max) + { + *option = parameter_reset; + return 0; + } + else + return 1; +} + + +/** + * Test/Demo code to cycle through a bunch of camera settings + * This code is pretty hacky so please don't complain!! + * It only does stuff that should have a visual impact (hence demo!) + * This will override any user supplied parameters + * + * Each call of this function will move on to the next setting + * + * @param camera Pointer to the camera to change settings on. + * @return 0 if reached end of complete sequence, !0 otherwise + */ + +int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera) +{ + static int parameter = 0; + static int parameter_option = parameter_reset; // which value the parameter currently has + + vcos_assert(camera); + + // We are going to cycle through all the relevant entries in the parameter block + // and send options to the camera. + if (parameter == 0) + { + // sharpness + if (update_cycle_parameter(¶meter_option, -100, 100, 10)) + raspicamcontrol_set_sharpness(camera, parameter_option); + else + { + raspicamcontrol_set_sharpness(camera, 0); + parameter++; + } + } + else + if (parameter == 1) + { + // contrast + if (update_cycle_parameter(¶meter_option, -100, 100, 10)) + raspicamcontrol_set_contrast(camera, parameter_option); + else + { + raspicamcontrol_set_contrast(camera, 0); + parameter++; + } + } + else + if (parameter == 2) + { + // brightness + if (update_cycle_parameter(¶meter_option, 0, 100, 10)) + raspicamcontrol_set_brightness(camera, parameter_option); + else + { + raspicamcontrol_set_brightness(camera, 50); + parameter++; + } + } + else + if (parameter == 3) + { + // contrast + if (update_cycle_parameter(¶meter_option, -100, 100, 10)) + raspicamcontrol_set_saturation(camera, parameter_option); + else + { + parameter++; + raspicamcontrol_set_saturation(camera, 0); + } + } + else + if (parameter == 4) + { + // EV + if (update_cycle_parameter(¶meter_option, -10, 10, 4)) + raspicamcontrol_set_exposure_compensation(camera, parameter_option); + else + { + raspicamcontrol_set_exposure_compensation(camera, 0); + parameter++; + } + } + else + if (parameter == 5) + { + // MMAL_PARAM_EXPOSUREMODE_T + if (update_cycle_parameter(¶meter_option, 0, exposure_map_size, 1)) + raspicamcontrol_set_exposure_mode(camera, exposure_map[parameter_option].mmal_mode); + else + { + raspicamcontrol_set_exposure_mode(camera, MMAL_PARAM_EXPOSUREMODE_AUTO); + parameter++; + } + } + else + if (parameter == 6) + { + // MMAL_PARAM_AWB_T + if (update_cycle_parameter(¶meter_option, 0, awb_map_size, 1)) + raspicamcontrol_set_awb_mode(camera, awb_map[parameter_option].mmal_mode); + else + { + raspicamcontrol_set_awb_mode(camera, MMAL_PARAM_AWBMODE_AUTO); + parameter++; + } + } + if (parameter == 7) + { + // MMAL_PARAM_IMAGEFX_T + if (update_cycle_parameter(¶meter_option, 0, imagefx_map_size, 1)) + raspicamcontrol_set_imageFX(camera, imagefx_map[parameter_option].mmal_mode); + else + { + raspicamcontrol_set_imageFX(camera, MMAL_PARAM_IMAGEFX_NONE); + parameter++; + } + } + if (parameter == 8) + { + MMAL_PARAM_COLOURFX_T colfx = {0,0,0}; + switch (parameter_option) + { + case parameter_reset : + parameter_option = 1; + colfx.u = 128; + colfx.v = 128; + break; + case 1 : + parameter_option = 2; + colfx.u = 100; + colfx.v = 200; + break; + case 2 : + parameter_option = parameter_reset; + colfx.enable = 0; + parameter++; + break; + } + raspicamcontrol_set_colourFX(camera, &colfx); + } + + // Orientation + if (parameter == 9) + { + switch (parameter_option) + { + case parameter_reset: + raspicamcontrol_set_rotation(camera, 90); + parameter_option = 1; + break; + + case 1 : + raspicamcontrol_set_rotation(camera, 180); + parameter_option = 2; + break; + + case 2 : + raspicamcontrol_set_rotation(camera, 270); + parameter_option = 3; + break; + + case 3 : + { + raspicamcontrol_set_rotation(camera, 0); + raspicamcontrol_set_flips(camera, 1,0); + parameter_option = 4; + break; + } + case 4 : + { + raspicamcontrol_set_flips(camera, 0,1); + parameter_option = 5; + break; + } + case 5 : + { + raspicamcontrol_set_flips(camera, 1, 1); + parameter_option = 6; + break; + } + case 6 : + { + raspicamcontrol_set_flips(camera, 0, 0); + parameter_option = parameter_reset; + parameter++; + break; + } + } + } + + if (parameter == 10) + { + parameter = 1; + return 0; + } + + return 1; +} + + + +/** + * Convert string to the MMAL parameter for exposure mode + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_PARAM_EXPOSUREMODE_T exposure_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, exposure_map, exposure_map_size); + + if( i != -1) + return (MMAL_PARAM_EXPOSUREMODE_T)i; + + vcos_log_error("Unknown exposure mode: %s", str); + return MMAL_PARAM_EXPOSUREMODE_AUTO; +} + +/** + * Convert string to the MMAL parameter for flicker avoid mode + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_PARAM_FLICKERAVOID_T flicker_avoid_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, flicker_avoid_map, flicker_avoid_map_size); + + if( i != -1) + return (MMAL_PARAM_FLICKERAVOID_T)i; + + vcos_log_error("Unknown flicker avoid mode: %s", str); + return MMAL_PARAM_FLICKERAVOID_OFF; +} + +/** + * Convert string to the MMAL parameter for AWB mode + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_PARAM_AWBMODE_T awb_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, awb_map, awb_map_size); + + if( i != -1) + return (MMAL_PARAM_AWBMODE_T)i; + + vcos_log_error("Unknown awb mode: %s", str); + return MMAL_PARAM_AWBMODE_AUTO; +} + +/** + * Convert string to the MMAL parameter for image effects mode + * @param str Incoming string to match + * @return MMAL parameter matching the strong, or the AUTO option if no match found + */ +MMAL_PARAM_IMAGEFX_T imagefx_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, imagefx_map, imagefx_map_size); + + if( i != -1) + return (MMAL_PARAM_IMAGEFX_T)i; + + vcos_log_error("Unknown image fx: %s", str); + return MMAL_PARAM_IMAGEFX_NONE; +} + +/** + * Convert string to the MMAL parameter for exposure metering mode + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_PARAM_EXPOSUREMETERINGMODE_T metering_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, metering_mode_map, metering_mode_map_size); + + if( i != -1) + return (MMAL_PARAM_EXPOSUREMETERINGMODE_T)i; + + vcos_log_error("Unknown metering mode: %s", str); + return MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; +} + +/** + * Convert string to the MMAL parameter for DRC level + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_PARAMETER_DRC_STRENGTH_T drc_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, drc_mode_map, drc_mode_map_size); + + if( i != -1) + return (MMAL_PARAMETER_DRC_STRENGTH_T)i; + + vcos_log_error("Unknown DRC level: %s", str); + return MMAL_PARAMETER_DRC_STRENGTH_OFF; +} + +/** + * Convert string to the MMAL parameter for exposure metering mode + * @param str Incoming string to match + * @return MMAL parameter matching the string, or the AUTO option if no match found + */ +static MMAL_STEREOSCOPIC_MODE_T stereo_mode_from_string(const char *str) +{ + int i = raspicli_map_xref(str, stereo_mode_map, stereo_mode_map_size); + + if( i != -1) + return (MMAL_STEREOSCOPIC_MODE_T)i; + + vcos_log_error("Unknown metering mode: %s", str); + return MMAL_STEREOSCOPIC_MODE_NONE; +} + +/** + * Parse a possible command pair - command and parameter + * @param arg1 Command + * @param arg2 Parameter (could be NULL) + * @return How many parameters were used, 0,1,2 + */ +int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2) +{ + int command_id, used = 0, num_parameters; + + if (!arg1) + return 0; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters); + + // If invalid command, or we are missing a parameter, drop out + if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL)) + return 0; + + switch (command_id) + { + case CommandSharpness : // sharpness - needs single number parameter + sscanf(arg2, "%d", ¶ms->sharpness); + used = 2; + break; + + case CommandContrast : // contrast - needs single number parameter + sscanf(arg2, "%d", ¶ms->contrast); + used = 2; + break; + + case CommandBrightness : // brightness - needs single number parameter + sscanf(arg2, "%d", ¶ms->brightness); + used = 2; + break; + + case CommandSaturation : // saturation - needs single number parameter + sscanf(arg2, "%d", ¶ms->saturation); + used = 2; + break; + + case CommandISO : // ISO - needs single number parameter + sscanf(arg2, "%d", ¶ms->ISO); + used = 2; + break; + + case CommandVideoStab : // video stabilisation - if here, its on + params->videoStabilisation = 1; + used = 1; + break; + + case CommandEVComp : // EV - needs single number parameter + sscanf(arg2, "%d", ¶ms->exposureCompensation); + used = 2; + break; + + case CommandExposure : // exposure mode - needs string + params->exposureMode = exposure_mode_from_string(arg2); + used = 2; + break; + + case CommandFlicker : // flicker avoid mode - needs string + params->flickerAvoidMode = flicker_avoid_mode_from_string(arg2); + used = 2; + break; + + case CommandAWB : // AWB mode - needs single number parameter + params->awbMode = awb_mode_from_string(arg2); + used = 2; + break; + + case CommandImageFX : // Image FX - needs string + params->imageEffect = imagefx_mode_from_string(arg2); + used = 2; + break; + + case CommandColourFX : // Colour FX - needs string "u:v" + sscanf(arg2, "%d:%d", ¶ms->colourEffects.u, ¶ms->colourEffects.v); + params->colourEffects.enable = 1; + used = 2; + break; + + case CommandMeterMode: + params->exposureMeterMode = metering_mode_from_string(arg2); + used = 2; + break; + + case CommandRotation : // Rotation - degree + sscanf(arg2, "%d", ¶ms->rotation); + used = 2; + break; + + case CommandHFlip : + params->hflip = 1; + used = 1; + break; + + case CommandVFlip : + params->vflip = 1; + used = 1; + break; + + case CommandROI : + { + double x,y,w,h; + int args; + + args = sscanf(arg2, "%lf,%lf,%lf,%lf", &x,&y,&w,&h); + + if (args != 4 || x > 1.0 || y > 1.0 || w > 1.0 || h > 1.0) + { + return 0; + } + + // Make sure we stay within bounds + if (x + w > 1.0) + w = 1 - x; + + if (y + h > 1.0) + h = 1 - y; + + params->roi.x = x; + params->roi.y = y; + params->roi.w = w; + params->roi.h = h; + + used = 2; + break; + } + + case CommandShutterSpeed : // Shutter speed needs single number parameter + { + sscanf(arg2, "%d", ¶ms->shutter_speed); + used = 2; + break; + } + + case CommandAwbGains : + { + double r,b; + int args; + + args = sscanf(arg2, "%lf,%lf", &r,&b); + + if (args != 2 || r > 8.0 || b > 8.0) + { + return 0; + } + + params->awb_gains_r = r; + params->awb_gains_b = b; + + used = 2; + break; + } + + case CommandDRCLevel: + { + params->drc_level = drc_mode_from_string(arg2); + used = 2; + break; + } + + case CommandStatsPass: + { + params->stats_pass = MMAL_TRUE; + used = 1; + break; + } + + case CommandAnnotate: + { + char dummy; + unsigned int bitmask; + // If parameter is a number, assume its a bitmask, otherwise a string + if (sscanf(arg2, "%u%c", &bitmask, &dummy) == 1) + { + params->enable_annotate |= bitmask; + } + else + { + params->enable_annotate |= ANNOTATE_USER_TEXT; + //copy string char by char and replace "\n" with newline character + unsigned char c; + char const *s = arg2; + char *t = ¶ms->annotate_string[0]; + int n=0; + while ((c = *s++) && n < MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1) + { + if (c == '\\' && *s) + { + switch (c = *s++) + { + case 'n': + c = '\n'; + break; + + default: + c = '\\'; + s--; + break; + } + } + *(t++) = c; + n++; + } + *t='\0'; + + //params->annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1] = '\0'; + } + used=2; + break; + } + + case CommandAnnotateExtras: + { + // 3 parameters - text size (6-80), text colour (Hex VVUUYY) and background colour (Hex VVUUYY) + sscanf(arg2, "%u,%X,%X", ¶ms->annotate_text_size, + ¶ms->annotate_text_colour, + ¶ms->annotate_bg_colour); + used=2; + break; + } + + case CommandStereoMode: + { + params->stereo_mode.mode = stereo_mode_from_string(arg2); + used = 2; + break; + } + + case CommandStereoDecimate: + { + params->stereo_mode.decimate = MMAL_TRUE; + used = 1; + break; + } + + case CommandStereoSwap: + { + params->stereo_mode.swap_eyes = MMAL_TRUE; + used = 1; + break; + } + + case CommandAnalogGain: + { + double gain; + int args; + + args = sscanf(arg2, "%lf", &gain); + + if (args != 1 || gain > 16.0) + { + return 0; + } + + params->analog_gain = gain; + + used = 2; + break; + } + case CommandDigitalGain: + { + double gain; + int args; + + args = sscanf(arg2, "%lf", &gain); + + if (args != 1 || gain > 64.0) + { + return 0; + } + + params->digital_gain = gain; + + used = 2; + break; + } + + + } + + return used; +} + +/** + * Display help for command line options + */ +void raspicamcontrol_display_help() +{ + int i; + + fprintf(stdout, "\nImage parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + fprintf(stdout, "\n\nNotes\n\nExposure mode options :\n%s", exposure_map[0].mode ); + + for (i=1;iexposureMode, exposure_map, exposure_map_size); + const char *fl_mode = raspicli_unmap_xref(params->flickerAvoidMode, flicker_avoid_map, flicker_avoid_map_size); + const char *awb_mode = raspicli_unmap_xref(params->awbMode, awb_map, awb_map_size); + const char *image_effect = raspicli_unmap_xref(params->imageEffect, imagefx_map, imagefx_map_size); + const char *metering_mode = raspicli_unmap_xref(params->exposureMeterMode, metering_mode_map, metering_mode_map_size); + + fprintf(stderr, "Sharpness %d, Contrast %d, Brightness %d\n", params->sharpness, params->contrast, params->brightness); + fprintf(stderr, "Saturation %d, ISO %d, Video Stabilisation %s, Exposure compensation %d\n", params->saturation, params->ISO, params->videoStabilisation ? "Yes": "No", params->exposureCompensation); + fprintf(stderr, "Exposure Mode '%s', AWB Mode '%s', Image Effect '%s'\n", exp_mode, awb_mode, image_effect); + fprintf(stderr, "Flicker Avoid Mode '%s'\n", fl_mode); + fprintf(stderr, "Metering Mode '%s', Colour Effect Enabled %s with U = %d, V = %d\n", metering_mode, params->colourEffects.enable ? "Yes":"No", params->colourEffects.u, params->colourEffects.v); + fprintf(stderr, "Rotation %d, hflip %s, vflip %s\n", params->rotation, params->hflip ? "Yes":"No",params->vflip ? "Yes":"No"); + fprintf(stderr, "ROI x %lf, y %f, w %f h %f\n", params->roi.x, params->roi.y, params->roi.w, params->roi.h); +} + +/** + * Convert a MMAL status return value to a simple boolean of success + * ALso displays a fault if code is not success + * + * @param status The error code to convert + * @return 0 if status is success, 1 otherwise + */ +int mmal_status_to_int(MMAL_STATUS_T status) +{ + if (status == MMAL_SUCCESS) + return 0; + else + { + switch (status) + { + case MMAL_ENOMEM : vcos_log_error("Out of memory"); break; + case MMAL_ENOSPC : vcos_log_error("Out of resources (other than memory)"); break; + case MMAL_EINVAL: vcos_log_error("Argument is invalid"); break; + case MMAL_ENOSYS : vcos_log_error("Function not implemented"); break; + case MMAL_ENOENT : vcos_log_error("No such file or directory"); break; + case MMAL_ENXIO : vcos_log_error("No such device or address"); break; + case MMAL_EIO : vcos_log_error("I/O error"); break; + case MMAL_ESPIPE : vcos_log_error("Illegal seek"); break; + case MMAL_ECORRUPT : vcos_log_error("Data is corrupt \attention FIXME: not POSIX"); break; + case MMAL_ENOTREADY :vcos_log_error("Component is not ready \attention FIXME: not POSIX"); break; + case MMAL_ECONFIG : vcos_log_error("Component is not configured \attention FIXME: not POSIX"); break; + case MMAL_EISCONN : vcos_log_error("Port is already connected "); break; + case MMAL_ENOTCONN : vcos_log_error("Port is disconnected"); break; + case MMAL_EAGAIN : vcos_log_error("Resource temporarily unavailable. Try again later"); break; + case MMAL_EFAULT : vcos_log_error("Bad address"); break; + default : vcos_log_error("Unknown status error"); break; + } + + return 1; + } +} + +/** + * Give the supplied parameter block a set of default values + * @params Pointer to parameter block + */ +void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params) +{ + vcos_assert(params); + + params->sharpness = 0; + params->contrast = 0; + params->brightness = 50; + params->saturation = 0; + params->ISO = 0; // 0 = auto + params->videoStabilisation = 0; + params->exposureCompensation = 0; + params->exposureMode = MMAL_PARAM_EXPOSUREMODE_AUTO; + params->flickerAvoidMode = MMAL_PARAM_FLICKERAVOID_OFF; + params->exposureMeterMode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; + params->awbMode = MMAL_PARAM_AWBMODE_AUTO; + params->imageEffect = MMAL_PARAM_IMAGEFX_NONE; + params->colourEffects.enable = 0; + params->colourEffects.u = 128; + params->colourEffects.v = 128; + params->rotation = 0; + params->hflip = params->vflip = 0; + params->roi.x = params->roi.y = 0.0; + params->roi.w = params->roi.h = 1.0; + params->shutter_speed = 0; // 0 = auto + params->awb_gains_r = 0; // Only have any function if AWB OFF is used. + params->awb_gains_b = 0; + params->drc_level = MMAL_PARAMETER_DRC_STRENGTH_OFF; + params->stats_pass = MMAL_FALSE; + params->enable_annotate = 0; + params->annotate_string[0] = '\0'; + params->annotate_text_size = 0; //Use firmware default + params->annotate_text_colour = -1; //Use firmware default + params->annotate_bg_colour = -1; //Use firmware default + params->stereo_mode.mode = MMAL_STEREOSCOPIC_MODE_NONE; + params->stereo_mode.decimate = MMAL_FALSE; + params->stereo_mode.swap_eyes = MMAL_FALSE; +} + +/** + * Get all the current camera parameters from specified camera component + * @param camera Pointer to camera component + * @param params Pointer to parameter block to accept settings + * @return 0 if successful, non-zero if unsuccessful + */ +int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params) +{ + vcos_assert(camera); + vcos_assert(params); + + if (!camera || !params) + return 1; + +/* TODO : Write these get functions + params->sharpness = raspicamcontrol_get_sharpness(camera); + params->contrast = raspicamcontrol_get_contrast(camera); + params->brightness = raspicamcontrol_get_brightness(camera); + params->saturation = raspicamcontrol_get_saturation(camera); + params->ISO = raspicamcontrol_get_ISO(camera); + params->videoStabilisation = raspicamcontrol_get_video_stabilisation(camera); + params->exposureCompensation = raspicamcontrol_get_exposure_compensation(camera); + params->exposureMode = raspicamcontrol_get_exposure_mode(camera); + params->flickerAvoidMode = raspicamcontrol_get_flicker_avoid_mode(camera); + params->awbMode = raspicamcontrol_get_awb_mode(camera); + params->imageEffect = raspicamcontrol_get_image_effect(camera); + params->colourEffects = raspicamcontrol_get_colour_effect(camera); + params->thumbnailConfig = raspicamcontrol_get_thumbnail_config(camera); +*/ + return 0; +} + +/** + * Set the specified camera to all the specified settings + * @param camera Pointer to camera component + * @param params Pointer to parameter block containing parameters + * @return 0 if successful, none-zero if unsuccessful. + */ +int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params) +{ + int result; + + result = raspicamcontrol_set_saturation(camera, params->saturation); + result += raspicamcontrol_set_sharpness(camera, params->sharpness); + result += raspicamcontrol_set_contrast(camera, params->contrast); + result += raspicamcontrol_set_brightness(camera, params->brightness); + result += raspicamcontrol_set_ISO(camera, params->ISO); + result += raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation); + result += raspicamcontrol_set_exposure_compensation(camera, params->exposureCompensation); + result += raspicamcontrol_set_exposure_mode(camera, params->exposureMode); + result += raspicamcontrol_set_flicker_avoid_mode(camera, params->flickerAvoidMode); + result += raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode); + result += raspicamcontrol_set_awb_mode(camera, params->awbMode); + result += raspicamcontrol_set_awb_gains(camera, params->awb_gains_r, params->awb_gains_b); + result += raspicamcontrol_set_imageFX(camera, params->imageEffect); + result += raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects); + //result += raspicamcontrol_set_thumbnail_parameters(camera, ¶ms->thumbnailConfig); TODO Not working for some reason + result += raspicamcontrol_set_rotation(camera, params->rotation); + result += raspicamcontrol_set_flips(camera, params->hflip, params->vflip); + result += raspicamcontrol_set_ROI(camera, params->roi); + result += raspicamcontrol_set_shutter_speed(camera, params->shutter_speed); + result += raspicamcontrol_set_DRC(camera, params->drc_level); + result += raspicamcontrol_set_stats_pass(camera, params->stats_pass); + result += raspicamcontrol_set_annotate(camera, params->enable_annotate, params->annotate_string, + params->annotate_text_size, + params->annotate_text_colour, + params->annotate_bg_colour); + result += raspicamcontrol_set_gains(camera, params->analog_gain, params->digital_gain); + + return result; +} + +/** + * Adjust the saturation level for images + * @param camera Pointer to camera component + * @param saturation Value to adjust, -100 to 100 + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation) +{ + int ret = 0; + + if (!camera) + return 1; + + if (saturation >= -100 && saturation <= 100) + { + MMAL_RATIONAL_T value = {saturation, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SATURATION, value)); + } + else + { + vcos_log_error("Invalid saturation value"); + ret = 1; + } + + return ret; +} + +/** + * Set the sharpness of the image + * @param camera Pointer to camera component + * @param sharpness Sharpness adjustment -100 to 100 + */ +int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness) +{ + int ret = 0; + + if (!camera) + return 1; + + if (sharpness >= -100 && sharpness <= 100) + { + MMAL_RATIONAL_T value = {sharpness, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SHARPNESS, value)); + } + else + { + vcos_log_error("Invalid sharpness value"); + ret = 1; + } + + return ret; +} + +/** + * Set the contrast adjustment for the image + * @param camera Pointer to camera component + * @param contrast Contrast adjustment -100 to 100 + * @return + */ +int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast) +{ + int ret = 0; + + if (!camera) + return 1; + + if (contrast >= -100 && contrast <= 100) + { + MMAL_RATIONAL_T value = {contrast, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_CONTRAST, value)); + } + else + { + vcos_log_error("Invalid contrast value"); + ret = 1; + } + + return ret; +} + +/** + * Adjust the brightness level for images + * @param camera Pointer to camera component + * @param brightness Value to adjust, 0 to 100 + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness) +{ + int ret = 0; + + if (!camera) + return 1; + + if (brightness >= 0 && brightness <= 100) + { + MMAL_RATIONAL_T value = {brightness, 100}; + ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_BRIGHTNESS, value)); + } + else + { + vcos_log_error("Invalid brightness value"); + ret = 1; + } + + return ret; +} + +/** + * Adjust the ISO used for images + * @param camera Pointer to camera component + * @param ISO Value to set TODO : + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_ISO, ISO)); +} + +/** + * Adjust the metering mode for images + * @param camera Pointer to camera component + * @param saturation Value from following + * - MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, + * - MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, + * - MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, + * - MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T m_mode ) +{ + MMAL_PARAMETER_EXPOSUREMETERINGMODE_T meter_mode = {{MMAL_PARAMETER_EXP_METERING_MODE,sizeof(meter_mode)}, + m_mode}; + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &meter_mode.hdr)); +} + + +/** + * Set the video stabilisation flag. Only used in video mode + * @param camera Pointer to camera component + * @param saturation Flag 0 off 1 on + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_boolean(camera->control, MMAL_PARAMETER_VIDEO_STABILISATION, vstabilisation)); +} + +/** + * Adjust the exposure compensation for images (EV) + * @param camera Pointer to camera component + * @param exp_comp Value to adjust, -10 to +10 + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_int32(camera->control, MMAL_PARAMETER_EXPOSURE_COMP , exp_comp)); +} + + +/** + * Set exposure mode for images + * @param camera Pointer to camera component + * @param mode Exposure mode to set from + * - MMAL_PARAM_EXPOSUREMODE_OFF, + * - MMAL_PARAM_EXPOSUREMODE_AUTO, + * - MMAL_PARAM_EXPOSUREMODE_NIGHT, + * - MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, + * - MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, + * - MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, + * - MMAL_PARAM_EXPOSUREMODE_SPORTS, + * - MMAL_PARAM_EXPOSUREMODE_SNOW, + * - MMAL_PARAM_EXPOSUREMODE_BEACH, + * - MMAL_PARAM_EXPOSUREMODE_VERYLONG, + * - MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, + * - MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, + * - MMAL_PARAM_EXPOSUREMODE_FIREWORKS, + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode) +{ + MMAL_PARAMETER_EXPOSUREMODE_T exp_mode = {{MMAL_PARAMETER_EXPOSURE_MODE,sizeof(exp_mode)}, mode}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &exp_mode.hdr)); +} + + +/** + * Set flicker avoid mode for images + * @param camera Pointer to camera component + * @param mode Exposure mode to set from + * - MMAL_PARAM_FLICKERAVOID_OFF, + * - MMAL_PARAM_FLICKERAVOID_AUTO, + * - MMAL_PARAM_FLICKERAVOID_50HZ, + * - MMAL_PARAM_FLICKERAVOID_60HZ, + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_flicker_avoid_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_FLICKERAVOID_T mode) +{ + MMAL_PARAMETER_FLICKERAVOID_T fl_mode = {{MMAL_PARAMETER_FLICKER_AVOID,sizeof(fl_mode)}, mode}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &fl_mode.hdr)); +} + + +/** + * Set the aWB (auto white balance) mode for images + * @param camera Pointer to camera component + * @param awb_mode Value to set from + * - MMAL_PARAM_AWBMODE_OFF, + * - MMAL_PARAM_AWBMODE_AUTO, + * - MMAL_PARAM_AWBMODE_SUNLIGHT, + * - MMAL_PARAM_AWBMODE_CLOUDY, + * - MMAL_PARAM_AWBMODE_SHADE, + * - MMAL_PARAM_AWBMODE_TUNGSTEN, + * - MMAL_PARAM_AWBMODE_FLUORESCENT, + * - MMAL_PARAM_AWBMODE_INCANDESCENT, + * - MMAL_PARAM_AWBMODE_FLASH, + * - MMAL_PARAM_AWBMODE_HORIZON, + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode) +{ + MMAL_PARAMETER_AWBMODE_T param = {{MMAL_PARAMETER_AWB_MODE,sizeof(param)}, awb_mode}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); +} + +int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain) +{ + MMAL_PARAMETER_AWB_GAINS_T param = {{MMAL_PARAMETER_CUSTOM_AWB_GAINS,sizeof(param)}, {0,0}, {0,0}}; + + if (!camera) + return 1; + + if (!r_gain || !b_gain) + return 0; + + param.r_gain.num = (unsigned int)(r_gain * 65536); + param.b_gain.num = (unsigned int)(b_gain * 65536); + param.r_gain.den = param.b_gain.den = 65536; + return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); +} + +/** + * Set the image effect for the images + * @param camera Pointer to camera component + * @param imageFX Value from + * - MMAL_PARAM_IMAGEFX_NONE, + * - MMAL_PARAM_IMAGEFX_NEGATIVE, + * - MMAL_PARAM_IMAGEFX_SOLARIZE, + * - MMAL_PARAM_IMAGEFX_POSTERIZE, + * - MMAL_PARAM_IMAGEFX_WHITEBOARD, + * - MMAL_PARAM_IMAGEFX_BLACKBOARD, + * - MMAL_PARAM_IMAGEFX_SKETCH, + * - MMAL_PARAM_IMAGEFX_DENOISE, + * - MMAL_PARAM_IMAGEFX_EMBOSS, + * - MMAL_PARAM_IMAGEFX_OILPAINT, + * - MMAL_PARAM_IMAGEFX_HATCH, + * - MMAL_PARAM_IMAGEFX_GPEN, + * - MMAL_PARAM_IMAGEFX_PASTEL, + * - MMAL_PARAM_IMAGEFX_WATERCOLOUR, + * - MMAL_PARAM_IMAGEFX_FILM, + * - MMAL_PARAM_IMAGEFX_BLUR, + * - MMAL_PARAM_IMAGEFX_SATURATION, + * - MMAL_PARAM_IMAGEFX_COLOURSWAP, + * - MMAL_PARAM_IMAGEFX_WASHEDOUT, + * - MMAL_PARAM_IMAGEFX_POSTERISE, + * - MMAL_PARAM_IMAGEFX_COLOURPOINT, + * - MMAL_PARAM_IMAGEFX_COLOURBALANCE, + * - MMAL_PARAM_IMAGEFX_CARTOON, + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX) +{ + MMAL_PARAMETER_IMAGEFX_T imgFX = {{MMAL_PARAMETER_IMAGE_EFFECT,sizeof(imgFX)}, imageFX}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &imgFX.hdr)); +} + +/* TODO :what to do with the image effects parameters? + MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {{MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS,sizeof(imfx_param)}, + imageFX, 0, {0}}; +mmal_port_parameter_set(camera->control, &imfx_param.hdr); + */ + +/** + * Set the colour effect for images (Set UV component) + * @param camera Pointer to camera component + * @param colourFX Contains enable state and U and V numbers to set (e.g. 128,128 = Black and white) + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX) +{ + MMAL_PARAMETER_COLOURFX_T colfx = {{MMAL_PARAMETER_COLOUR_EFFECT,sizeof(colfx)}, 0, 0, 0}; + + if (!camera) + return 1; + + colfx.enable = colourFX->enable; + colfx.u = colourFX->u; + colfx.v = colourFX->v; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &colfx.hdr)); + +} + + +/** + * Set the rotation of the image + * @param camera Pointer to camera component + * @param rotation Degree of rotation (any number, but will be converted to 0,90,180 or 270 only) + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation) +{ + int ret; + int my_rotation = ((rotation % 360 ) / 90) * 90; + + ret = mmal_port_parameter_set_int32(camera->output[0], MMAL_PARAMETER_ROTATION, my_rotation); + mmal_port_parameter_set_int32(camera->output[1], MMAL_PARAMETER_ROTATION, my_rotation); + mmal_port_parameter_set_int32(camera->output[2], MMAL_PARAMETER_ROTATION, my_rotation); + + return ret; +} + +/** + * Set the flips state of the image + * @param camera Pointer to camera component + * @param hflip If true, horizontally flip the image + * @param vflip If true, vertically flip the image + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip) +{ + MMAL_PARAMETER_MIRROR_T mirror = {{MMAL_PARAMETER_MIRROR, sizeof(MMAL_PARAMETER_MIRROR_T)}, MMAL_PARAM_MIRROR_NONE}; + + if (hflip && vflip) + mirror.value = MMAL_PARAM_MIRROR_BOTH; + else + if (hflip) + mirror.value = MMAL_PARAM_MIRROR_HORIZONTAL; + else + if (vflip) + mirror.value = MMAL_PARAM_MIRROR_VERTICAL; + + mmal_port_parameter_set(camera->output[0], &mirror.hdr); + mmal_port_parameter_set(camera->output[1], &mirror.hdr); + return mmal_port_parameter_set(camera->output[2], &mirror.hdr); +} + +/** + * Set the ROI of the sensor to use for captures/preview + * @param camera Pointer to camera component + * @param rect Normalised coordinates of ROI rectangle + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect) +{ + MMAL_PARAMETER_INPUT_CROP_T crop = {{MMAL_PARAMETER_INPUT_CROP, sizeof(MMAL_PARAMETER_INPUT_CROP_T)}}; + + crop.rect.x = (65536 * rect.x); + crop.rect.y = (65536 * rect.y); + crop.rect.width = (65536 * rect.w); + crop.rect.height = (65536 * rect.h); + + return mmal_port_parameter_set(camera->control, &crop.hdr); +} + +/** + * Zoom in and Zoom out by changing ROI + * @param camera Pointer to camera component + * @param zoom_command zoom command enum + * @return 0 if successful, non-zero otherwise + */ +int raspicamcontrol_zoom_in_zoom_out(MMAL_COMPONENT_T *camera, ZOOM_COMMAND_T zoom_command, PARAM_FLOAT_RECT_T *roi) { + MMAL_PARAMETER_INPUT_CROP_T crop; + crop.hdr.id = MMAL_PARAMETER_INPUT_CROP; + crop.hdr.size = sizeof(crop); + + if (mmal_port_parameter_get(camera->control, &crop.hdr) != MMAL_SUCCESS) + { + vcos_log_error("mmal_port_parameter_get(camera->control, &crop.hdr) failed, skip it"); + return 0; + } + + if (zoom_command == ZOOM_IN) + { + if (crop.rect.width <= (zoom_full_16P16 + zoom_increment_16P16)) + { + crop.rect.width = zoom_full_16P16; + crop.rect.height = zoom_full_16P16; + } + else + { + crop.rect.width -= zoom_increment_16P16; + crop.rect.height -= zoom_increment_16P16; + } + } + else if (zoom_command == ZOOM_OUT) + { + unsigned int increased_size = crop.rect.width + zoom_increment_16P16; + if (increased_size < crop.rect.width) //overflow + { + crop.rect.width = 65536; + crop.rect.height = 65536; + } + else + { + crop.rect.width = increased_size; + crop.rect.height = increased_size; + } + } + + if (zoom_command == ZOOM_RESET) + { + crop.rect.x = 0; + crop.rect.y = 0; + crop.rect.width = 65536; + crop.rect.height = 65536; + } + else + { + unsigned int centered_top_coordinate = (65536 - crop.rect.width) / 2; + crop.rect.x = centered_top_coordinate; + crop.rect.y = centered_top_coordinate; + } + + int ret = mmal_status_to_int(mmal_port_parameter_set(camera->control, &crop.hdr)); + + if (ret == 0) { + roi->x = roi->y = (double)crop.rect.x/65536; + roi->w = roi->h = (double)crop.rect.width/65536; + } + else + { + vcos_log_error("Failed to set crop values, x/y: %u, w/h: %u", crop.rect.x, crop.rect.width); + ret = 1; + } + + return ret; +} + +/** + * Adjust the exposure time used for images + * @param camera Pointer to camera component + * @param shutter speed in microseconds + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_SHUTTER_SPEED, speed)); +} + +/** + * Adjust the Dynamic range compression level + * @param camera Pointer to camera component + * @param strength Strength of DRC to apply + * MMAL_PARAMETER_DRC_STRENGTH_OFF + * MMAL_PARAMETER_DRC_STRENGTH_LOW + * MMAL_PARAMETER_DRC_STRENGTH_MEDIUM + * MMAL_PARAMETER_DRC_STRENGTH_HIGH + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength) +{ + MMAL_PARAMETER_DRC_T drc = {{MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, sizeof(MMAL_PARAMETER_DRC_T)}, strength}; + + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &drc.hdr)); +} + +int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass) +{ + if (!camera) + return 1; + + return mmal_status_to_int(mmal_port_parameter_set_boolean(camera->control, MMAL_PARAMETER_CAPTURE_STATS_PASS, stats_pass)); +} + + +/** + * Set the annotate data + * @param camera Pointer to camera component + * @param Bitmask of required annotation data. 0 for off. + * @param If set, a pointer to text string to use instead of bitmask, max length 32 characters + * + * @return 0 if successful, non-zero if any parameters out of range + */ +int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int settings, const char *string, + const int text_size, const int text_colour, const int bg_colour) +{ + MMAL_PARAMETER_CAMERA_ANNOTATE_V3_T annotate = + {{MMAL_PARAMETER_ANNOTATE, sizeof(MMAL_PARAMETER_CAMERA_ANNOTATE_V3_T)}}; + + if (settings) + { + time_t t = time(NULL); + struct tm tm = *localtime(&t); + char tmp[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3]; + int process_datetime = 1; + + annotate.enable = 1; + + if (settings & (ANNOTATE_APP_TEXT | ANNOTATE_USER_TEXT)) + { + if ((settings & (ANNOTATE_TIME_TEXT | ANNOTATE_DATE_TEXT)) && strchr(string,'%') != NULL) + { //string contains strftime parameter? + strftime(annotate.text, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3, string, &tm ); + process_datetime = 0; + }else{ + strncpy(annotate.text, string, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3); + } + annotate.text[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1] = '\0'; + } + + if (process_datetime && (settings & ANNOTATE_TIME_TEXT)) + { + if(strlen(annotate.text)){ + strftime(tmp, 32, " %X", &tm ); + }else{ + strftime(tmp, 32, "%X", &tm ); + } + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text) - 1); + } + + if (process_datetime && (settings & ANNOTATE_DATE_TEXT)) + { + if(strlen(annotate.text)){ + strftime(tmp, 32, " %x", &tm ); + }else{ + strftime(tmp, 32, "%x", &tm ); + } + strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text) - 1); + } + + if (settings & ANNOTATE_SHUTTER_SETTINGS) + annotate.show_shutter = MMAL_TRUE; + + if (settings & ANNOTATE_GAIN_SETTINGS) + annotate.show_analog_gain = MMAL_TRUE; + + if (settings & ANNOTATE_LENS_SETTINGS) + annotate.show_lens = MMAL_TRUE; + + if (settings & ANNOTATE_CAF_SETTINGS) + annotate.show_caf = MMAL_TRUE; + + if (settings & ANNOTATE_MOTION_SETTINGS) + annotate.show_motion = MMAL_TRUE; + + if (settings & ANNOTATE_FRAME_NUMBER) + annotate.show_frame_num = MMAL_TRUE; + + if (settings & ANNOTATE_BLACK_BACKGROUND) + annotate.enable_text_background = MMAL_TRUE; + + annotate.text_size = text_size; + + if (text_colour != -1) + { + annotate.custom_text_colour = MMAL_TRUE; + annotate.custom_text_Y = text_colour&0xff; + annotate.custom_text_U = (text_colour>>8)&0xff; + annotate.custom_text_V = (text_colour>>16)&0xff; + } + else + annotate.custom_text_colour = MMAL_FALSE; + + if (bg_colour != -1) + { + annotate.custom_background_colour = MMAL_TRUE; + annotate.custom_background_Y = bg_colour&0xff; + annotate.custom_background_U = (bg_colour>>8)&0xff; + annotate.custom_background_V = (bg_colour>>16)&0xff; + } + else + annotate.custom_background_colour = MMAL_FALSE; + } + else + annotate.enable = 0; + + return mmal_status_to_int(mmal_port_parameter_set(camera->control, &annotate.hdr)); +} + +int raspicamcontrol_set_stereo_mode(MMAL_PORT_T *port, MMAL_PARAMETER_STEREOSCOPIC_MODE_T *stereo_mode) +{ + MMAL_PARAMETER_STEREOSCOPIC_MODE_T stereo = { {MMAL_PARAMETER_STEREOSCOPIC_MODE, sizeof(stereo)}, + MMAL_STEREOSCOPIC_MODE_NONE, MMAL_FALSE, MMAL_FALSE }; + if (stereo_mode->mode != MMAL_STEREOSCOPIC_MODE_NONE) + { + stereo.mode = stereo_mode->mode; + stereo.decimate = stereo_mode->decimate; + stereo.swap_eyes = stereo_mode->swap_eyes; + } + return mmal_status_to_int(mmal_port_parameter_set(port, &stereo.hdr)); +} + +int raspicamcontrol_set_gains(MMAL_COMPONENT_T *camera, float analog, float digital) +{ + MMAL_RATIONAL_T rational = {0,65536}; + MMAL_STATUS_T status; + + if (!camera) + return 1; + + rational.num = (unsigned int)(analog * 65536); + status = mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_ANALOG_GAIN, rational); + if (status != MMAL_SUCCESS) + return mmal_status_to_int(status); + + rational.num = (unsigned int)(digital * 65536); + status = mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_DIGITAL_GAIN, rational); + return mmal_status_to_int(status); +} + +/** + * Asked GPU how much memory it has allocated + * + * @return amount of memory in MB + */ +static int raspicamcontrol_get_mem_gpu(void) +{ + char response[80] = ""; + int gpu_mem = 0; + if (vc_gencmd(response, sizeof response, "get_mem gpu") == 0) + vc_gencmd_number_property(response, "gpu", &gpu_mem); + return gpu_mem; +} + +/** + * Ask GPU about its camera abilities + * @param supported None-zero if software supports the camera + * @param detected None-zero if a camera has been detected + */ +static void raspicamcontrol_get_camera(int *supported, int *detected) +{ + char response[80] = ""; + if (vc_gencmd(response, sizeof response, "get_camera") == 0) + { + if (supported) + vc_gencmd_number_property(response, "supported", supported); + if (detected) + vc_gencmd_number_property(response, "detected", detected); + } +} + +/** + * Check to see if camera is supported, and we have allocated enough meooryAsk GPU about its camera abilities + * @param supported None-zero if software supports the camera + * @param detected None-zero if a camera has been detected + */ +void raspicamcontrol_check_configuration(int min_gpu_mem) +{ + int gpu_mem = raspicamcontrol_get_mem_gpu(); + int supported = 0, detected = 0; + raspicamcontrol_get_camera(&supported, &detected); + if (!supported) + vcos_log_error("Camera is not enabled in this build. Try running \"sudo raspi-config\" and ensure that \"camera\" has been enabled\n"); + else if (gpu_mem < min_gpu_mem) + vcos_log_error("Only %dM of gpu_mem is configured. Try running \"sudo raspi-config\" and ensure that \"memory_split\" has a value of %d or greater\n", gpu_mem, min_gpu_mem); + else if (!detected) + vcos_log_error("Camera is not detected. Please check carefully the camera module is installed correctly\n"); + else + vcos_log_error("Failed to run camera app. Please check for firmware updates\n"); +} + diff --git a/host_applications/linux/apps/raspicam/RaspiCamControl.h b/host_applications/linux/apps/raspicam/RaspiCamControl.h new file mode 100755 index 0000000..0286d9f --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiCamControl.h @@ -0,0 +1,236 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RASPICAMCONTROL_H_ +#define RASPICAMCONTROL_H_ + +/* Various parameters + * + * Exposure Mode + * MMAL_PARAM_EXPOSUREMODE_OFF, + MMAL_PARAM_EXPOSUREMODE_AUTO, + MMAL_PARAM_EXPOSUREMODE_NIGHT, + MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, + MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, + MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, + MMAL_PARAM_EXPOSUREMODE_SPORTS, + MMAL_PARAM_EXPOSUREMODE_SNOW, + MMAL_PARAM_EXPOSUREMODE_BEACH, + MMAL_PARAM_EXPOSUREMODE_VERYLONG, + MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, + MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, + MMAL_PARAM_EXPOSUREMODE_FIREWORKS, + * + * Flicker Avoid Mode + * MMAL_PARAM_FLICKERAVOID_OFF, + MMAL_PARAM_FLICKERAVOID_AUTO, + MMAL_PARAM_FLICKERAVOID_50HZ, + MMAL_PARAM_FLICKERAVOID_60HZ, + * + * AWB Mode + * MMAL_PARAM_AWBMODE_OFF, + MMAL_PARAM_AWBMODE_AUTO, + MMAL_PARAM_AWBMODE_SUNLIGHT, + MMAL_PARAM_AWBMODE_CLOUDY, + MMAL_PARAM_AWBMODE_SHADE, + MMAL_PARAM_AWBMODE_TUNGSTEN, + MMAL_PARAM_AWBMODE_FLUORESCENT, + MMAL_PARAM_AWBMODE_INCANDESCENT, + MMAL_PARAM_AWBMODE_FLASH, + MMAL_PARAM_AWBMODE_HORIZON, + * + * Image FX + MMAL_PARAM_IMAGEFX_NONE, + MMAL_PARAM_IMAGEFX_NEGATIVE, + MMAL_PARAM_IMAGEFX_SOLARIZE, + MMAL_PARAM_IMAGEFX_POSTERIZE, + MMAL_PARAM_IMAGEFX_WHITEBOARD, + MMAL_PARAM_IMAGEFX_BLACKBOARD, + MMAL_PARAM_IMAGEFX_SKETCH, + MMAL_PARAM_IMAGEFX_DENOISE, + MMAL_PARAM_IMAGEFX_EMBOSS, + MMAL_PARAM_IMAGEFX_OILPAINT, + MMAL_PARAM_IMAGEFX_HATCH, + MMAL_PARAM_IMAGEFX_GPEN, + MMAL_PARAM_IMAGEFX_PASTEL, + MMAL_PARAM_IMAGEFX_WATERCOLOUR, + MMAL_PARAM_IMAGEFX_FILM, + MMAL_PARAM_IMAGEFX_BLUR, + MMAL_PARAM_IMAGEFX_SATURATION, + MMAL_PARAM_IMAGEFX_COLOURSWAP, + MMAL_PARAM_IMAGEFX_WASHEDOUT, + MMAL_PARAM_IMAGEFX_POSTERISE, + MMAL_PARAM_IMAGEFX_COLOURPOINT, + MMAL_PARAM_IMAGEFX_COLOURBALANCE, + MMAL_PARAM_IMAGEFX_CARTOON, + + */ + +/// Annotate bitmask options +/// Supplied by user on command line +#define ANNOTATE_USER_TEXT 1 +/// Supplied by app using this module +#define ANNOTATE_APP_TEXT 2 +/// Insert current date +#define ANNOTATE_DATE_TEXT 4 +// Insert current time +#define ANNOTATE_TIME_TEXT 8 + +#define ANNOTATE_SHUTTER_SETTINGS 16 +#define ANNOTATE_CAF_SETTINGS 32 +#define ANNOTATE_GAIN_SETTINGS 64 +#define ANNOTATE_LENS_SETTINGS 128 +#define ANNOTATE_MOTION_SETTINGS 256 +#define ANNOTATE_FRAME_NUMBER 512 +#define ANNOTATE_BLACK_BACKGROUND 1024 + + +// There isn't actually a MMAL structure for the following, so make one +typedef struct mmal_param_colourfx_s +{ + int enable; /// Turn colourFX on or off + int u,v; /// U and V to use +} MMAL_PARAM_COLOURFX_T; + +typedef struct mmal_param_thumbnail_config_s +{ + int enable; + int width,height; + int quality; +} MMAL_PARAM_THUMBNAIL_CONFIG_T; + +typedef struct param_float_rect_s +{ + double x; + double y; + double w; + double h; +} PARAM_FLOAT_RECT_T; + +/// struct contain camera settings +typedef struct raspicam_camera_parameters_s +{ + int sharpness; /// -100 to 100 + int contrast; /// -100 to 100 + int brightness; /// 0 to 100 + int saturation; /// -100 to 100 + int ISO; /// TODO : what range? + int videoStabilisation; /// 0 or 1 (false or true) + int exposureCompensation; /// -10 to +10 ? + MMAL_PARAM_EXPOSUREMODE_T exposureMode; + MMAL_PARAM_EXPOSUREMETERINGMODE_T exposureMeterMode; + MMAL_PARAM_AWBMODE_T awbMode; + MMAL_PARAM_IMAGEFX_T imageEffect; + MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imageEffectsParameters; + MMAL_PARAM_COLOURFX_T colourEffects; + MMAL_PARAM_FLICKERAVOID_T flickerAvoidMode; + int rotation; /// 0-359 + int hflip; /// 0 or 1 + int vflip; /// 0 or 1 + PARAM_FLOAT_RECT_T roi; /// region of interest to use on the sensor. Normalised [0,1] values in the rect + int shutter_speed; /// 0 = auto, otherwise the shutter speed in ms + float awb_gains_r; /// AWB red gain + float awb_gains_b; /// AWB blue gain + MMAL_PARAMETER_DRC_STRENGTH_T drc_level; // Strength of Dynamic Range compression to apply + MMAL_BOOL_T stats_pass; /// Stills capture statistics pass on/off + int enable_annotate; /// Flag to enable the annotate, 0 = disabled, otherwise a bitmask of what needs to be displayed + char annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2]; /// String to use for annotate - overrides certain bitmask settings + int annotate_text_size; // Text size for annotation + int annotate_text_colour; // Text colour for annotation + int annotate_bg_colour; // Background colour for annotation + MMAL_PARAMETER_STEREOSCOPIC_MODE_T stereo_mode; + float analog_gain; // Analog gain + float digital_gain; // Digital gain +} RASPICAM_CAMERA_PARAMETERS; + +typedef enum { + ZOOM_IN, ZOOM_OUT, ZOOM_RESET +} ZOOM_COMMAND_T; + + +void raspicamcontrol_check_configuration(int min_gpu_mem); + +int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2); +void raspicamcontrol_display_help(); +int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera); + +int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params); +int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params); +void raspicamcontrol_dump_parameters(const RASPICAM_CAMERA_PARAMETERS *params); + +void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params); + +void raspicamcontrol_check_configuration(int min_gpu_mem); + +// Individual setting functions +int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation); +int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness); +int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast); +int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness); +int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO); +int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T mode); +int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation); +int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp); +int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode); +int raspicamcontrol_set_flicker_avoid_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_FLICKERAVOID_T mode); +int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode); +int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain); +int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX); +int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX); +int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation); +int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip); +int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect); +int raspicamcontrol_zoom_in_zoom_out(MMAL_COMPONENT_T *camera, ZOOM_COMMAND_T zoom_command, PARAM_FLOAT_RECT_T *roi); +int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed_ms); +int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength); +int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass); +int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int bitmask, const char *string, + const int text_size, const int text_colour, const int bg_colour); +int raspicamcontrol_set_stereo_mode(MMAL_PORT_T *port, MMAL_PARAMETER_STEREOSCOPIC_MODE_T *stereo_mode); +int raspicamcontrol_set_gains(MMAL_COMPONENT_T *camera, float analog, float digital); + +//Individual getting functions +int raspicamcontrol_get_saturation(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_sharpness(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_contrast(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_brightness(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_ISO(MMAL_COMPONENT_T *camera); +MMAL_PARAM_EXPOSUREMETERINGMODE_T raspicamcontrol_get_metering_mode(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_video_stabilisation(MMAL_COMPONENT_T *camera); +int raspicamcontrol_get_exposure_compensation(MMAL_COMPONENT_T *camera); +MMAL_PARAM_THUMBNAIL_CONFIG_T raspicamcontrol_get_thumbnail_parameters(MMAL_COMPONENT_T *camera); +MMAL_PARAM_EXPOSUREMODE_T raspicamcontrol_get_exposure_mode(MMAL_COMPONENT_T *camera); +MMAL_PARAM_FLICKERAVOID_T raspicamcontrol_get_flicker_avoid_mode(MMAL_COMPONENT_T *camera); +MMAL_PARAM_AWBMODE_T raspicamcontrol_get_awb_mode(MMAL_COMPONENT_T *camera); +MMAL_PARAM_IMAGEFX_T raspicamcontrol_get_imageFX(MMAL_COMPONENT_T *camera); +MMAL_PARAM_COLOURFX_T raspicamcontrol_get_colourFX(MMAL_COMPONENT_T *camera); + + + + +#endif /* RASPICAMCONTROL_H_ */ diff --git a/host_applications/linux/apps/raspicam/RaspiPreview.c b/host_applications/linux/apps/raspicam/RaspiPreview.c new file mode 100755 index 0000000..7656c87 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiPreview.c @@ -0,0 +1,281 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" + +#include "RaspiPreview.h" +#include "RaspiCLI.h" + +#define CommandPreview 1 +#define CommandFullScreen 2 +#define CommandOpacity 3 +#define CommandDisablePreview 4 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandPreview, "-preview", "p", "Preview window settings <'x,y,w,h'>", 1 }, + { CommandFullScreen, "-fullscreen", "f", "Fullscreen preview mode", 0 }, + { CommandOpacity, "-opacity", "op", "Preview window opacity (0-255)", 1}, + { CommandDisablePreview,"-nopreview", "n", "Do not display a preview window", 0}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +/** + * Create the preview component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state) +{ + MMAL_COMPONENT_T *preview = 0; + MMAL_PORT_T *preview_port = NULL; + MMAL_STATUS_T status; + + if (!state->wantPreview) + { + // No preview required, so create a null sink component to take its place + status = mmal_component_create("vc.null_sink", &preview); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create null sink component"); + goto error; + } + } + else + { + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, + &preview); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create preview component"); + goto error; + } + + if (!preview->input_num) + { + status = MMAL_ENOSYS; + vcos_log_error("No input ports found on component"); + goto error; + } + + preview_port = preview->input[0]; + + MMAL_DISPLAYREGION_T param; + param.hdr.id = MMAL_PARAMETER_DISPLAYREGION; + param.hdr.size = sizeof(MMAL_DISPLAYREGION_T); + + param.set = MMAL_DISPLAY_SET_LAYER; + param.layer = PREVIEW_LAYER; + + param.set |= MMAL_DISPLAY_SET_ALPHA; + param.alpha = state->opacity; + + if (state->wantFullScreenPreview) + { + param.set |= MMAL_DISPLAY_SET_FULLSCREEN; + param.fullscreen = 1; + } + else + { + param.set |= (MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN); + param.fullscreen = 0; + param.dest_rect = state->previewWindow; + } + + status = mmal_port_parameter_set(preview_port, ¶m.hdr); + + if (status != MMAL_SUCCESS && status != MMAL_ENOSYS) + { + vcos_log_error("unable to set preview port parameters (%u)", status); + goto error; + } + } + + /* Enable component */ + status = mmal_component_enable(preview); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable preview/null sink component (%u)", status); + goto error; + } + + state->preview_component = preview; + + return status; + +error: + + if (preview) + mmal_component_destroy(preview); + + return status; +} + + +/** + * Destroy the preview component + * + * @param state Pointer to state control struct + * + */ +void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state) +{ + if (state->preview_component) + { + mmal_component_destroy(state->preview_component); + state->preview_component = NULL; + } +} + +/** + * Assign set of default parameters to the passed in parameter block + * + * @param state Pointer to parameter block + * + */ +void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state) +{ + state->wantPreview = 1; + state->wantFullScreenPreview = 1; + state->opacity = 255; + state->previewWindow.x = 0; + state->previewWindow.y = 0; + state->previewWindow.width = 1024; + state->previewWindow.height = 768; + state->preview_component = NULL; +} + +/** + * Dump parameters as human readable to stdout + * + * @param state Pointer to parameter block + * + */ +void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *state) +{ + fprintf(stderr, "Preview %s, Full screen %s\n", state->wantPreview ? "Yes" : "No", + state->wantFullScreenPreview ? "Yes" : "No"); + + fprintf(stderr, "Preview window %d,%d,%d,%d\nOpacity %d\n", state->previewWindow.x, + state->previewWindow.y, state->previewWindow.width, + state->previewWindow.height, state->opacity); +}; + +/** + * Parse a possible command pair - command and parameter + * @param arg1 Command + * @param arg2 Parameter (could be NULL) + * @return How many parameters were used, 0,1,2 + */ +int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *params, const char *arg1, const char *arg2) +{ + int command_id, used = 0, num_parameters; + + if (!arg1) + return 0; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters); + + // If invalid command, or we are missing a parameter, drop out + if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL)) + return 0; + + switch (command_id) + { + case CommandPreview: // Preview window + { + int tmp; + + params->wantPreview = 1; + + tmp = sscanf(arg2, "%d,%d,%d,%d", + ¶ms->previewWindow.x, ¶ms->previewWindow.y, + ¶ms->previewWindow.width, ¶ms->previewWindow.height); + + // Failed to get any window parameters, so revert to full screen + if (tmp == 0) + params->wantFullScreenPreview = 1; + else + params->wantFullScreenPreview = 0; + + used = 2; + + break; + } + + case CommandFullScreen: // Want full screen preview mode (overrides display rect) + params->wantPreview = 1; + params->wantFullScreenPreview = 1; + + used = 1; + break; + + case CommandOpacity: // Define preview window opacity + if (sscanf(arg2, "%u", ¶ms->opacity) != 1) + params->opacity = 255; + else + used = 2; + break; + + case CommandDisablePreview: // Turn off preview output + params->wantPreview = 0; + used = 1; + break; + } + + return used; +} + +/** + * Display help for command line options + */ +void raspipreview_display_help() +{ + fprintf(stdout, "\nPreview parameter commands\n\n"); + raspicli_display_help(cmdline_commands, cmdline_commands_size); +} diff --git a/host_applications/linux/apps/raspicam/RaspiPreview.h b/host_applications/linux/apps/raspicam/RaspiPreview.h new file mode 100755 index 0000000..409dbff --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiPreview.h @@ -0,0 +1,67 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RASPIPREVIEW_H_ +#define RASPIPREVIEW_H_ + +/// Layer that preview window should be displayed on +#define PREVIEW_LAYER 2 + +// Frames rates of 0 implies variable, but denominator needs to be 1 to prevent div by 0 +#define PREVIEW_FRAME_RATE_NUM 0 +#define PREVIEW_FRAME_RATE_DEN 1 + +#define FULL_RES_PREVIEW_FRAME_RATE_NUM 0 +#define FULL_RES_PREVIEW_FRAME_RATE_DEN 1 + +#define FULL_FOV_PREVIEW_16x9_X 1280 +#define FULL_FOV_PREVIEW_16x9_Y 720 + +#define FULL_FOV_PREVIEW_4x3_X 1296 +#define FULL_FOV_PREVIEW_4x3_Y 972 + +#define FULL_FOV_PREVIEW_FRAME_RATE_NUM 0 +#define FULL_FOV_PREVIEW_FRAME_RATE_DEN 1 + +typedef struct +{ + int wantPreview; /// Display a preview + int wantFullScreenPreview; /// 0 is use previewRect, non-zero to use full screen + int opacity; /// Opacity of window - 0 = transparent, 255 = opaque + MMAL_RECT_T previewWindow; /// Destination rectangle for the preview window. + MMAL_COMPONENT_T *preview_component; /// Pointer to the created preview display component +} RASPIPREVIEW_PARAMETERS; + +MMAL_STATUS_T raspipreview_create(RASPIPREVIEW_PARAMETERS *state); +void raspipreview_destroy(RASPIPREVIEW_PARAMETERS *state); +void raspipreview_set_defaults(RASPIPREVIEW_PARAMETERS *state); +void raspipreview_dump_parameters(RASPIPREVIEW_PARAMETERS *state); +int raspipreview_parse_cmdline(RASPIPREVIEW_PARAMETERS *params, const char *arg1, const char *arg2); +void raspipreview_display_help(); + +#endif /* RASPIPREVIEW_H_ */ diff --git a/host_applications/linux/apps/raspicam/RaspiStill.c b/host_applications/linux/apps/raspicam/RaspiStill.c new file mode 100755 index 0000000..0c3cf3f --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiStill.c @@ -0,0 +1,2169 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiStill.c + * Command line program to capture a still frame and encode it to file. + * Also optionally display a preview/viewfinder of current camera input. + * + * \date 31 Jan 2013 + * \Author: James Hughes + * + * Description + * + * 3 components are created; camera, preview and JPG encoder. + * Camera component has three ports, preview, video and stills. + * This program connects preview and stills to the preview and jpg + * encoder. Using mmal we don't need to worry about buffers between these + * components, but we do need to handle buffers from the encoder, which + * are simply written straight to the file in the requisite buffer callback. + * + * We use the RaspiCamControl code to handle the specific camera settings. + */ + +// We use some GNU extensions (asprintf, basename) +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION_STRING "v1.3.11" + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" +#include "interface/mmal/mmal_parameters_camera.h" + + +#include "RaspiCamControl.h" +#include "RaspiPreview.h" +#include "RaspiCLI.h" +#include "RaspiTex.h" + +#include + +// Standard port setting for the camera component +#define MMAL_CAMERA_PREVIEW_PORT 0 +#define MMAL_CAMERA_VIDEO_PORT 1 +#define MMAL_CAMERA_CAPTURE_PORT 2 + + +// Stills format information +// 0 implies variable +#define STILLS_FRAME_RATE_NUM 0 +#define STILLS_FRAME_RATE_DEN 1 + +/// Video render needs at least 2 buffers. +#define VIDEO_OUTPUT_BUFFERS_NUM 3 + +#define MAX_USER_EXIF_TAGS 32 +#define MAX_EXIF_PAYLOAD_LENGTH 128 + +/// Frame advance method +#define FRAME_NEXT_SINGLE 0 +#define FRAME_NEXT_TIMELAPSE 1 +#define FRAME_NEXT_KEYPRESS 2 +#define FRAME_NEXT_FOREVER 3 +#define FRAME_NEXT_GPIO 4 +#define FRAME_NEXT_SIGNAL 5 +#define FRAME_NEXT_IMMEDIATELY 6 + + +int mmal_status_to_int(MMAL_STATUS_T status); +static void signal_handler(int signal_number); + + +/** Structure containing all state information for the current run + */ +typedef struct +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + char camera_name[MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN]; // Name of the camera sensor + int quality; /// JPEG quality setting (1-100) + int wantRAW; /// Flag for whether the JPEG metadata also contains the RAW bayer image + char *filename; /// filename of output file + char *linkname; /// filename of output file + int frameStart; /// First number of frame output counter + MMAL_PARAM_THUMBNAIL_CONFIG_T thumbnailConfig; + int verbose; /// !0 if want detailed run information + int demoMode; /// Run app in demo mode + int demoInterval; /// Interval between camera settings changes + MMAL_FOURCC_T encoding; /// Encoding to use for the output file. + const char *exifTags[MAX_USER_EXIF_TAGS]; /// Array of pointers to tags supplied from the command line + int numExifTags; /// Number of supplied tags + int enableExifTags; /// Enable/Disable EXIF tags in output + int timelapse; /// Delay between each picture in timelapse mode. If 0, disable timelapse + int fullResPreview; /// If set, the camera preview port runs at capture resolution. Reduces fps. + int frameNextMethod; /// Which method to use to advance to next frame + int useGL; /// Render preview using OpenGL + int glCapture; /// Save the GL frame-buffer instead of camera output + int settings; /// Request settings from the camera + int cameraNum; /// Camera number + int burstCaptureMode; /// Enable burst mode + int sensor_mode; /// Sensor mode. 0=auto. Check docs/forum for modes selected by other values. + int datetime; /// Use DateTime instead of frame# + int timestamp; /// Use timestamp instead of frame# + int restart_interval; /// JPEG restart interval. 0 for none. + + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component + MMAL_COMPONENT_T *encoder_component; /// Pointer to the encoder component + MMAL_COMPONENT_T *null_sink_component; /// Pointer to the null sink component + MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview + MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder + + MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port + + RASPITEX_STATE raspitex_state; /// GL renderer state and parameters + +} RASPISTILL_STATE; + +/** Struct used to pass information in encoder port userdata to callback + */ +typedef struct +{ + FILE *file_handle; /// File handle to write buffer data to. + VCOS_SEMAPHORE_T complete_semaphore; /// semaphore which is posted when we reach end of frame (indicates end of capture or fault) + RASPISTILL_STATE *pstate; /// pointer to our state in case required in callback +} PORT_USERDATA; + +static void display_valid_parameters(char *app_name); +static void store_exif_tag(RASPISTILL_STATE *state, const char *exif_tag); + +/// Comamnd ID's and Structure defining our command line options +#define CommandHelp 0 +#define CommandWidth 1 +#define CommandHeight 2 +#define CommandQuality 3 +#define CommandRaw 4 +#define CommandOutput 5 +#define CommandVerbose 6 +#define CommandTimeout 7 +#define CommandThumbnail 8 +#define CommandDemoMode 9 +#define CommandEncoding 10 +#define CommandExifTag 11 +#define CommandTimelapse 12 +#define CommandFullResPreview 13 +#define CommandLink 14 +#define CommandKeypress 15 +#define CommandSignal 16 +#define CommandGL 17 +#define CommandGLCapture 18 +#define CommandSettings 19 +#define CommandCamSelect 20 +#define CommandBurstMode 21 +#define CommandSensorMode 22 +#define CommandDateTime 23 +#define CommandTimeStamp 24 +#define CommandFrameStart 25 +#define CommandRestartInterval 26 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width ", 1 }, + { CommandHeight, "-height", "h", "Set image height ", 1 }, + { CommandQuality, "-quality", "q", "Set jpeg quality <0 to 100>", 1 }, + { CommandRaw, "-raw", "r", "Add raw bayer data to jpeg metadata", 0 }, + { CommandOutput, "-output", "o", "Output filename (to write to stdout, use '-o -'). If not specified, no file is saved", 1 }, + { CommandLink, "-latest", "l", "Link latest complete image to filename ", 1}, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) before takes picture and shuts down (if not specified, set to 5s)", 1 }, + { CommandThumbnail,"-thumb", "th", "Set thumbnail parameters (x:y:quality) or none", 1}, + { CommandDemoMode,"-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 0}, + { CommandEncoding,"-encoding", "e", "Encoding to use for output file (jpg, bmp, gif, png)", 1}, + { CommandExifTag, "-exif", "x", "EXIF tag to apply to captures (format as 'key=value') or none", 1}, + { CommandTimelapse,"-timelapse", "tl", "Timelapse mode. Takes a picture every ms. %d == frame number (Try: -o img_%04d.jpg)", 1}, + { CommandFullResPreview,"-fullpreview","fp", "Run the preview using the still capture resolution (may reduce preview fps)", 0}, + { CommandKeypress,"-keypress", "k", "Wait between captures for a ENTER, X then ENTER to exit", 0}, + { CommandSignal, "-signal", "s", "Wait between captures for a SIGUSR1 or SIGUSR2 from another process", 0}, + { CommandGL, "-gl", "g", "Draw preview to texture instead of using video render component", 0}, + { CommandGLCapture, "-glcapture","gc", "Capture the GL frame-buffer instead of the camera image", 0}, + { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0}, + { CommandCamSelect, "-camselect","cs", "Select camera . Default 0", 1 }, + { CommandBurstMode, "-burst", "bm", "Enable 'burst capture mode'", 0}, + { CommandSensorMode,"-mode", "md", "Force sensor mode. 0=auto. See docs for other modes available", 1}, + { CommandDateTime, "-datetime", "dt", "Replace output pattern (%d) with DateTime (MonthDayHourMinSec)", 0}, + { CommandTimeStamp, "-timestamp", "ts", "Replace output pattern (%d) with unix timestamp (seconds since 1970)", 0}, + { CommandFrameStart,"-framestart","fs", "Starting frame number in output pattern(%d)", 1}, + { CommandRestartInterval, "-restart","rs","JPEG Restart interval (default of 0 for none)", 1}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +static struct +{ + char *format; + MMAL_FOURCC_T encoding; +} encoding_xref[] = +{ + {"jpg", MMAL_ENCODING_JPEG}, + {"bmp", MMAL_ENCODING_BMP}, + {"gif", MMAL_ENCODING_GIF}, + {"png", MMAL_ENCODING_PNG} +}; + +static int encoding_xref_size = sizeof(encoding_xref) / sizeof(encoding_xref[0]); + + +static struct +{ + char *description; + int nextFrameMethod; +} next_frame_description[] = +{ + {"Single capture", FRAME_NEXT_SINGLE}, + {"Capture on timelapse", FRAME_NEXT_TIMELAPSE}, + {"Capture on keypress", FRAME_NEXT_KEYPRESS}, + {"Run forever", FRAME_NEXT_FOREVER}, + {"Capture on GPIO", FRAME_NEXT_GPIO}, + {"Capture on signal", FRAME_NEXT_SIGNAL}, +}; + +static int next_frame_description_size = sizeof(next_frame_description) / sizeof(next_frame_description[0]); + +static void set_sensor_defaults(RASPISTILL_STATE *state) +{ + MMAL_COMPONENT_T *camera_info; + MMAL_STATUS_T status; + + // Default to the OV5647 setup + strncpy(state->camera_name, "OV5647", MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN); + + // Try to get the camera name and maximum supported resolution + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA_INFO, &camera_info); + if (status == MMAL_SUCCESS) + { + MMAL_PARAMETER_CAMERA_INFO_T param; + param.hdr.id = MMAL_PARAMETER_CAMERA_INFO; + param.hdr.size = sizeof(param)-4; // Deliberately undersize to check firmware veresion + status = mmal_port_parameter_get(camera_info->control, ¶m.hdr); + + if (status != MMAL_SUCCESS) + { + // Running on newer firmware + param.hdr.size = sizeof(param); + status = mmal_port_parameter_get(camera_info->control, ¶m.hdr); + if (status == MMAL_SUCCESS && param.num_cameras > state->cameraNum) + { + // Take the parameters from the first camera listed. + if (state->width == 0) + state->width = param.cameras[state->cameraNum].max_width; + if (state->height == 0) + state->height = param.cameras[state->cameraNum].max_height; + strncpy(state->camera_name, param.cameras[state->cameraNum].camera_name, MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN); + state->camera_name[MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN-1] = 0; + } + else + vcos_log_error("Cannot read camera info, keeping the defaults for OV5647"); + } + else + { + // Older firmware + // Nothing to do here, keep the defaults for OV5647 + } + + mmal_component_destroy(camera_info); + } + else + { + vcos_log_error("Failed to create camera_info component"); + } + //Command line hasn't specified a resolution, and we failed to + //get a default resolution from camera_info. Assume OV5647 full res + if (state->width == 0) + state->width = 2592; + if (state->height == 0) + state->height = 1944; +} + +/** + * Assign a default set of parameters to the state passed in + * + * @param state Pointer to state structure to assign defaults to + */ +static void default_status(RASPISTILL_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + state->timeout = 5000; // 5s delay before take image + state->quality = 85; + state->wantRAW = 0; + state->filename = NULL; + state->linkname = NULL; + state->frameStart = 0; + state->verbose = 0; + state->thumbnailConfig.enable = 1; + state->thumbnailConfig.width = 64; + state->thumbnailConfig.height = 48; + state->thumbnailConfig.quality = 35; + state->demoMode = 0; + state->demoInterval = 250; // ms + state->camera_component = NULL; + state->encoder_component = NULL; + state->preview_connection = NULL; + state->encoder_connection = NULL; + state->encoder_pool = NULL; + state->encoding = MMAL_ENCODING_JPEG; + state->numExifTags = 0; + state->enableExifTags = 1; + state->timelapse = 0; + state->fullResPreview = 0; + state->frameNextMethod = FRAME_NEXT_SINGLE; + state->useGL = 0; + state->glCapture = 0; + state->settings = 0; + state->cameraNum = 0; + state->burstCaptureMode=0; + state->sensor_mode = 0; + state->datetime = 0; + state->timestamp = 0; + state->restart_interval = 0; + + // Setup preview window defaults + raspipreview_set_defaults(&state->preview_parameters); + + // Set up the camera_parameters to default + raspicamcontrol_set_defaults(&state->camera_parameters); + + // Set initial GL preview state + raspitex_set_defaults(&state->raspitex_state); +} + +/** + * Dump image state parameters to stderr. Used for debugging + * + * @param state Pointer to state structure to assign defaults to + */ +static void dump_status(RASPISTILL_STATE *state) +{ + int i; + + if (!state) + { + vcos_assert(0); + return; + } + + fprintf(stderr, "Width %d, Height %d, quality %d, filename %s\n", state->width, + state->height, state->quality, state->filename); + fprintf(stderr, "Time delay %d, Raw %s\n", state->timeout, + state->wantRAW ? "yes" : "no"); + fprintf(stderr, "Thumbnail enabled %s, width %d, height %d, quality %d\n", + state->thumbnailConfig.enable ? "Yes":"No", state->thumbnailConfig.width, + state->thumbnailConfig.height, state->thumbnailConfig.quality); + fprintf(stderr, "Link to latest frame enabled "); + if (state->linkname) + { + fprintf(stderr, " yes, -> %s\n", state->linkname); + } + else + { + fprintf(stderr, " no\n"); + } + fprintf(stderr, "Full resolution preview %s\n", state->fullResPreview ? "Yes": "No"); + + fprintf(stderr, "Capture method : "); + for (i=0;iframeNextMethod == next_frame_description[i].nextFrameMethod) + fprintf(stderr, "%s", next_frame_description[i].description); + } + fprintf(stderr, "\n\n"); + + if (state->enableExifTags) + { + if (state->numExifTags) + { + fprintf(stderr, "User supplied EXIF tags :\n"); + + for (i=0;inumExifTags;i++) + { + fprintf(stderr, "%s", state->exifTags[i]); + if (i != state->numExifTags-1) + fprintf(stderr, ","); + } + fprintf(stderr, "\n\n"); + } + } + else + fprintf(stderr, "EXIF tags disabled\n"); + + raspipreview_dump_parameters(&state->preview_parameters); + raspicamcontrol_dump_parameters(&state->camera_parameters); +} + +/** + * Parse the incoming command line and put resulting parameters in to the state + * + * @param argc Number of arguments in command line + * @param argv Array of pointers to strings from command line + * @param state Pointer to state structure to assign any discovered parameters to + * @return non-0 if failed for some reason, 0 otherwise + */ +static int parse_cmdline(int argc, const char **argv, RASPISTILL_STATE *state) +{ + // Parse the command line arguments. + // We are looking for -- or - + + int valid = 1; + int i; + + for (i = 1; i < argc && valid; i++) + { + int command_id, num_parameters; + + if (!argv[i]) + continue; + + if (argv[i][0] != '-') + { + valid = 0; + continue; + } + + // Assume parameter is valid until proven otherwise + valid = 1; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters); + + // If we found a command but are missing a parameter, continue (and we will drop out of the loop) + if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) ) + continue; + + // We are now dealing with a command line option + switch (command_id) + { + case CommandHelp: + display_valid_parameters(basename(argv[0])); + // exit straight away if help requested + return -1; + + case CommandWidth: // Width > 0 + if (sscanf(argv[i + 1], "%u", &state->width) != 1) + valid = 0; + else + i++; + break; + + case CommandHeight: // Height > 0 + if (sscanf(argv[i + 1], "%u", &state->height) != 1) + valid = 0; + else + i++; + break; + + case CommandQuality: // Quality = 1-100 + if (sscanf(argv[i + 1], "%u", &state->quality) == 1) + { + if (state->quality > 100) + { + fprintf(stderr, "Setting max quality = 100\n"); + state->quality = 100; + } + i++; + } + else + valid = 0; + + break; + + case CommandRaw: // Add raw bayer data in metadata + state->wantRAW = 1; + break; + + case CommandOutput: // output filename + { + int len = strlen(argv[i + 1]); + if (len) + { + //We use sprintf to append the frame number for timelapse mode + //Ensure that any % is either %% or %d. + const char *percent = argv[i+1]; + while(valid && *percent && (percent=strchr(percent, '%')) != NULL) + { + int digits=0; + percent++; + while(isdigit(*percent)) + { + percent++; + digits++; + } + if(!((*percent == '%' && !digits) || *percent == 'd')) + { + valid = 0; + fprintf(stderr, "Filename contains %% characters, but not %%d or %%%% - sorry, will fail\n"); + } + percent++; + } + state->filename = malloc(len + 10); // leave enough space for any timelapse generated changes to filename + vcos_assert(state->filename); + if (state->filename) + strncpy(state->filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandLink : + { + int len = strlen(argv[i+1]); + if (len) + { + state->linkname = malloc(len + 10); + vcos_assert(state->linkname); + if (state->linkname) + strncpy(state->linkname, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + + } + + case CommandFrameStart: // use a staring value != 0 + { + if (sscanf(argv[i + 1], "%d", &state->frameStart) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandVerbose: // display lots of data during run + state->verbose = 1; + break; + case CommandDateTime: // use datetime + state->datetime = 1; + break; + case CommandTimeStamp: // use timestamp + state->timestamp = 1; + break; + + case CommandTimeout: // Time to run viewfinder for before taking picture, in seconds + { + if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) + { + // Ensure that if previously selected CommandKeypress we don't overwrite it + if (state->timeout == 0 && state->frameNextMethod == FRAME_NEXT_SINGLE) + state->frameNextMethod = FRAME_NEXT_FOREVER; + + i++; + } + else + valid = 0; + break; + } + case CommandThumbnail : // thumbnail parameters - needs string "x:y:quality" + if ( strcmp( argv[ i + 1 ], "none" ) == 0 ) + { + state->thumbnailConfig.enable = 0; + } + else + { + sscanf(argv[i + 1], "%d:%d:%d", + &state->thumbnailConfig.width, + &state->thumbnailConfig.height, + &state->thumbnailConfig.quality); + } + i++; + break; + + case CommandDemoMode: // Run in demo mode - no capture + { + // Demo mode might have a timing parameter + // so check if a) we have another parameter, b) its not the start of the next option + if (i + 1 < argc && argv[i+1][0] != '-') + { + if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1) + { + // TODO : What limits do we need for timeout? + state->demoMode = 1; + i++; + } + else + valid = 0; + } + else + { + state->demoMode = 1; + } + + break; + } + + case CommandEncoding : + { + int len = strlen(argv[i + 1]); + valid = 0; + + if (len) + { + int j; + for (j=0;jencoding = encoding_xref[j].encoding; + valid = 1; + i++; + break; + } + } + } + break; + } + + case CommandExifTag: + if ( strcmp( argv[ i + 1 ], "none" ) == 0 ) + { + state->enableExifTags = 0; + } + else + { + store_exif_tag(state, argv[i+1]); + } + i++; + break; + + case CommandTimelapse: + if (sscanf(argv[i + 1], "%u", &state->timelapse) != 1) + valid = 0; + else + { + if (state->timelapse) + state->frameNextMethod = FRAME_NEXT_TIMELAPSE; + else + state->frameNextMethod = FRAME_NEXT_IMMEDIATELY; + + + i++; + } + break; + + case CommandFullResPreview: + state->fullResPreview = 1; + break; + + case CommandKeypress: // Set keypress between capture mode + state->frameNextMethod = FRAME_NEXT_KEYPRESS; + break; + + case CommandSignal: // Set SIGUSR1 & SIGUSR2 between capture mode + state->frameNextMethod = FRAME_NEXT_SIGNAL; + // Reenable the signal + signal(SIGUSR1, signal_handler); + signal(SIGUSR2, signal_handler); + break; + + case CommandGL: + state->useGL = 1; + break; + + case CommandGLCapture: + state->glCapture = 1; + break; + + case CommandSettings: + state->settings = 1; + break; + + + case CommandCamSelect: //Select camera input port + { + if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandBurstMode: + state->burstCaptureMode=1; + break; + + case CommandSensorMode: + { + if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandRestartInterval: + { + if (sscanf(argv[i + 1], "%u", &state->restart_interval) == 1) + { + i++; + } + else + valid = 0; + break; + } + + default: + { + // Try parsing for any image specific parameters + // result indicates how many parameters were used up, 0,1,2 + // but we adjust by -1 as we have used one already + const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL; + int parms_used = raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg); + + // Still unused, try preview options + if (!parms_used) + parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + + // Still unused, try GL preview options + if (!parms_used) + parms_used = raspitex_parse_cmdline(&state->raspitex_state, &argv[i][1], second_arg); + + // If no parms were used, this must be a bad parameters + if (!parms_used) + valid = 0; + else + i += parms_used - 1; + + break; + } + } + } + + /* GL preview parameters use preview parameters as defaults unless overriden */ + if (! state->raspitex_state.gl_win_defined) + { + state->raspitex_state.x = state->preview_parameters.previewWindow.x; + state->raspitex_state.y = state->preview_parameters.previewWindow.y; + state->raspitex_state.width = state->preview_parameters.previewWindow.width; + state->raspitex_state.height = state->preview_parameters.previewWindow.height; + } + /* Also pass the preview information through so GL renderer can determine + * the real resolution of the multi-media image */ + state->raspitex_state.preview_x = state->preview_parameters.previewWindow.x; + state->raspitex_state.preview_y = state->preview_parameters.previewWindow.y; + state->raspitex_state.preview_width = state->preview_parameters.previewWindow.width; + state->raspitex_state.preview_height = state->preview_parameters.previewWindow.height; + state->raspitex_state.opacity = state->preview_parameters.opacity; + state->raspitex_state.verbose = state->verbose; + + if (!valid) + { + fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]); + return 1; + } + + return 0; +} + +/** + * Display usage information for the application to stdout + * + * @param app_name String to display as the application name + */ +static void display_valid_parameters(char *app_name) +{ + fprintf(stdout, "Runs camera for specific time, and take JPG capture at end if requested\n\n"); + fprintf(stdout, "usage: %s [options]\n\n", app_name); + + fprintf(stdout, "Image parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + // Help for preview options + raspipreview_display_help(); + + // Now display any help information from the camcontrol code + raspicamcontrol_display_help(); + + // Now display GL preview help + raspitex_display_help(); + + fprintf(stdout, "\n"); + + return; +} + +/** + * buffer header callback function for camera control + * + * No actions taken in current version + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED) + { + MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data; + switch (param->hdr.id) { + case MMAL_PARAMETER_CAMERA_SETTINGS: + { + MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param; + vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u", + settings->exposure, + settings->analog_gain.num, settings->analog_gain.den, + settings->digital_gain.num, settings->digital_gain.den); + vcos_log_error("AWB R=%u/%u, B=%u/%u", + settings->awb_red_gain.num, settings->awb_red_gain.den, + settings->awb_blue_gain.num, settings->awb_blue_gain.den + ); + } + break; + } + } + else if (buffer->cmd == MMAL_EVENT_ERROR) + { + vcos_log_error("No data received from sensor. Check all connections, including the Sunny one on the camera board"); + } + else + vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); + + mmal_buffer_header_release(buffer); +} + +/** + * buffer header callback function for encoder + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + int complete = 0; + + // We pass our file handle and other stuff in via the userdata field. + + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = buffer->length; + + if (buffer->length && pData->file_handle) + { + mmal_buffer_header_mem_lock(buffer); + + bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle); + + mmal_buffer_header_mem_unlock(buffer); + } + + // We need to check we wrote what we wanted - it's possible we have run out of storage. + if (bytes_written != buffer->length) + { + vcos_log_error("Unable to write buffer to file - aborting"); + complete = 1; + } + + // Now flag if we have completed + if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED)) + complete = 1; + } + else + { + vcos_log_error("Received a encoder buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_BUFFER_HEADER_T *new_buffer; + + new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue); + + if (new_buffer) + { + status = mmal_port_send_buffer(port, new_buffer); + } + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return a buffer to the encoder port"); + } + + if (complete) + vcos_semaphore_post(&(pData->complete_semaphore)); +} + +/** + * Create the camera component, set up its ports + * + * @param state Pointer to state control struct. camera_component member set to the created camera_component if successful. + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_camera_component(RASPISTILL_STATE *state) +{ + MMAL_COMPONENT_T *camera = 0; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_STATUS_T status; + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create camera component"); + goto error; + } + + status = raspicamcontrol_set_stereo_mode(camera->output[0], &state->camera_parameters.stereo_mode); + status += raspicamcontrol_set_stereo_mode(camera->output[1], &state->camera_parameters.stereo_mode); + status += raspicamcontrol_set_stereo_mode(camera->output[2], &state->camera_parameters.stereo_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set stereo mode : error %d", status); + goto error; + } + + MMAL_PARAMETER_INT32_T camera_num = + {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, state->cameraNum}; + + status = mmal_port_parameter_set(camera->control, &camera_num.hdr); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not select camera : error %d", status); + goto error; + } + + if (!camera->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera doesn't have output ports"); + goto error; + } + + status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, state->sensor_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set sensor mode : error %d", status); + goto error; + } + + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + if (state->settings) + { + MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request = + {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)}, + MMAL_PARAMETER_CAMERA_SETTINGS, 1}; + + status = mmal_port_parameter_set(camera->control, &change_event_request.hdr); + if ( status != MMAL_SUCCESS ) + { + vcos_log_error("No camera settings events"); + } + } + + // Enable the camera, and tell it its control callback function + status = mmal_port_enable(camera->control, camera_control_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable control port : error %d", status); + goto error; + } + + // set up the camera configuration + { + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = + { + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->width, + .max_stills_h = state->height, + .stills_yuv422 = 0, + .one_shot_stills = 1, + .max_preview_video_w = state->preview_parameters.previewWindow.width, + .max_preview_video_h = state->preview_parameters.previewWindow.height, + .num_preview_video_frames = 3, + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC + }; + + if (state->fullResPreview) + { + cam_config.max_preview_video_w = state->width; + cam_config.max_preview_video_h = state->height; + } + + mmal_port_parameter_set(camera->control, &cam_config.hdr); + } + + raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + + // Now set up the port formats + + format = preview_port->format; + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 166, 1000 }, {999, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + if (state->fullResPreview) + { + // In this mode we are forcing the preview to be generated from the full capture resolution. + // This runs at a max of 15fps with the OV5647 sensor. + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = FULL_RES_PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = FULL_RES_PREVIEW_FRAME_RATE_DEN; + } + else + { + // Use a full FOV 4:3 mode + format->es->video.width = VCOS_ALIGN_UP(state->preview_parameters.previewWindow.width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->preview_parameters.previewWindow.height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->preview_parameters.previewWindow.width; + format->es->video.crop.height = state->preview_parameters.previewWindow.height; + format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; + } + + status = mmal_port_format_commit(preview_port); + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto error; + } + + // Set the same format on the video port (which we don't use here) + mmal_format_full_copy(video_port->format, format); + status = mmal_port_format_commit(video_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera video format couldn't be set"); + goto error; + } + + // Ensure there are enough buffers to avoid dropping frames + if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + format = still_port->format; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(still_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 167, 1000 }, {999, 1000}}; + mmal_port_parameter_set(still_port, &fps_range.hdr); + } + // Set our stills format on the stills (for encoder) port + format->encoding = MMAL_ENCODING_OPAQUE; + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = STILLS_FRAME_RATE_NUM; + format->es->video.frame_rate.den = STILLS_FRAME_RATE_DEN; + + + status = mmal_port_format_commit(still_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera still format couldn't be set"); + goto error; + } + + /* Ensure there are enough buffers to avoid dropping frames */ + if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + /* Enable component */ + status = mmal_component_enable(camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera component couldn't be enabled"); + goto error; + } + + if (state->useGL) + { + status = raspitex_configure_preview_port(&state->raspitex_state, preview_port); + if (status != MMAL_SUCCESS) + { + fprintf(stderr, "Failed to configure preview port for GL rendering"); + goto error; + } + } + + state->camera_component = camera; + + if (state->verbose) + fprintf(stderr, "Camera component done\n"); + + return status; + +error: + + if (camera) + mmal_component_destroy(camera); + + return status; +} + +/** + * Destroy the camera component + * + * @param state Pointer to state control struct + * + */ +static void destroy_camera_component(RASPISTILL_STATE *state) +{ + if (state->camera_component) + { + mmal_component_destroy(state->camera_component); + state->camera_component = NULL; + } +} + +/** + * Create the encoder component, set up its ports + * + * @param state Pointer to state control struct. encoder_component member set to the created camera_component if successful. + * + * @return a MMAL_STATUS, MMAL_SUCCESS if all OK, something else otherwise + */ +static MMAL_STATUS_T create_encoder_component(RASPISTILL_STATE *state) +{ + MMAL_COMPONENT_T *encoder = 0; + MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create JPEG encoder component"); + goto error; + } + + if (!encoder->input_num || !encoder->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("JPEG encoder doesn't have input/output ports"); + goto error; + } + + encoder_input = encoder->input[0]; + encoder_output = encoder->output[0]; + + // We want same format on input and output + mmal_format_copy(encoder_output->format, encoder_input->format); + + // Specify out output format + encoder_output->format->encoding = state->encoding; + + encoder_output->buffer_size = encoder_output->buffer_size_recommended; + + if (encoder_output->buffer_size < encoder_output->buffer_size_min) + encoder_output->buffer_size = encoder_output->buffer_size_min; + + encoder_output->buffer_num = encoder_output->buffer_num_recommended; + + if (encoder_output->buffer_num < encoder_output->buffer_num_min) + encoder_output->buffer_num = encoder_output->buffer_num_min; + + // Commit the port changes to the output port + status = mmal_port_format_commit(encoder_output); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set format on video encoder output port"); + goto error; + } + + // Set the JPEG quality level + status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, state->quality); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set JPEG quality"); + goto error; + } + + // Set the JPEG restart interval + status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_RESTART_INTERVAL, state->restart_interval); + + if (state->restart_interval && status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set JPEG restart interval"); + goto error; + } + + // Set up any required thumbnail + { + MMAL_PARAMETER_THUMBNAIL_CONFIG_T param_thumb = {{MMAL_PARAMETER_THUMBNAIL_CONFIGURATION, sizeof(MMAL_PARAMETER_THUMBNAIL_CONFIG_T)}, 0, 0, 0, 0}; + + if ( state->thumbnailConfig.enable && + state->thumbnailConfig.width > 0 && state->thumbnailConfig.height > 0 ) + { + // Have a valid thumbnail defined + param_thumb.enable = 1; + param_thumb.width = state->thumbnailConfig.width; + param_thumb.height = state->thumbnailConfig.height; + param_thumb.quality = state->thumbnailConfig.quality; + } + status = mmal_port_parameter_set(encoder->control, ¶m_thumb.hdr); + } + + // Enable component + status = mmal_component_enable(encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable video encoder component"); + goto error; + } + + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); + } + + state->encoder_pool = pool; + state->encoder_component = encoder; + + if (state->verbose) + fprintf(stderr, "Encoder component done\n"); + + return status; + + error: + + if (encoder) + mmal_component_destroy(encoder); + + return status; +} + +/** + * Destroy the encoder component + * + * @param state Pointer to state control struct + * + */ +static void destroy_encoder_component(RASPISTILL_STATE *state) +{ + // Get rid of any port buffers first + if (state->encoder_pool) + { + mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool); + } + + if (state->encoder_component) + { + mmal_component_destroy(state->encoder_component); + state->encoder_component = NULL; + } +} + + +/** + * Add an exif tag to the capture + * + * @param state Pointer to state control struct + * @param exif_tag String containing a "key=value" pair. + * @return Returns a MMAL_STATUS_T giving result of operation + */ +static MMAL_STATUS_T add_exif_tag(RASPISTILL_STATE *state, const char *exif_tag) +{ + MMAL_STATUS_T status; + MMAL_PARAMETER_EXIF_T *exif_param = (MMAL_PARAMETER_EXIF_T*)calloc(sizeof(MMAL_PARAMETER_EXIF_T) + MAX_EXIF_PAYLOAD_LENGTH, 1); + + vcos_assert(state); + vcos_assert(state->encoder_component); + + // Check to see if the tag is present or is indeed a key=value pair. + if (!exif_tag || strchr(exif_tag, '=') == NULL || strlen(exif_tag) > MAX_EXIF_PAYLOAD_LENGTH-1) + return MMAL_EINVAL; + + exif_param->hdr.id = MMAL_PARAMETER_EXIF; + + strncpy((char*)exif_param->data, exif_tag, MAX_EXIF_PAYLOAD_LENGTH-1); + + exif_param->hdr.size = sizeof(MMAL_PARAMETER_EXIF_T) + strlen((char*)exif_param->data); + + status = mmal_port_parameter_set(state->encoder_component->output[0], &exif_param->hdr); + + free(exif_param); + + return status; +} + +/** + * Add a basic set of EXIF tags to the capture + * Make, Time etc + * + * @param state Pointer to state control struct + * + */ +static void add_exif_tags(RASPISTILL_STATE *state) +{ + time_t rawtime; + struct tm *timeinfo; + char model_buf[32]; + char time_buf[32]; + char exif_buf[128]; + int i; + + + snprintf(model_buf, 32, "IFD0.Model=RP_%s", state->camera_name); + add_exif_tag(state, model_buf); + add_exif_tag(state, "IFD0.Make=RaspberryPi"); + + time(&rawtime); + timeinfo = localtime(&rawtime); + + snprintf(time_buf, sizeof(time_buf), + "%04d:%02d:%02d %02d:%02d:%02d", + timeinfo->tm_year+1900, + timeinfo->tm_mon+1, + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + timeinfo->tm_sec); + + snprintf(exif_buf, sizeof(exif_buf), "EXIF.DateTimeDigitized=%s", time_buf); + add_exif_tag(state, exif_buf); + + snprintf(exif_buf, sizeof(exif_buf), "EXIF.DateTimeOriginal=%s", time_buf); + add_exif_tag(state, exif_buf); + + snprintf(exif_buf, sizeof(exif_buf), "IFD0.DateTime=%s", time_buf); + add_exif_tag(state, exif_buf); + + // Now send any user supplied tags + + for (i=0;inumExifTags && i < MAX_USER_EXIF_TAGS;i++) + { + if (state->exifTags[i]) + { + add_exif_tag(state, state->exifTags[i]); + } + } +} + +/** + * Stores an EXIF tag in the state, incrementing various pointers as necessary. + * Any tags stored in this way will be added to the image file when add_exif_tags + * is called + * + * Will not store if run out of storage space + * + * @param state Pointer to state control struct + * @param exif_tag EXIF tag string + * + */ +static void store_exif_tag(RASPISTILL_STATE *state, const char *exif_tag) +{ + if (state->numExifTags < MAX_USER_EXIF_TAGS) + { + state->exifTags[state->numExifTags] = exif_tag; + state->numExifTags++; + } +} + +/** + * Connect two specific ports together + * + * @param output_port Pointer the output port + * @param input_port Pointer the input port + * @param Pointer to a mmal connection pointer, reassigned if function successful + * @return Returns a MMAL_STATUS_T giving result of operation + * + */ +static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) +{ + MMAL_STATUS_T status; + + status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); + + if (status == MMAL_SUCCESS) + { + status = mmal_connection_enable(*connection); + if (status != MMAL_SUCCESS) + mmal_connection_destroy(*connection); + } + + return status; +} + + +/** + * Allocates and generates a filename based on the + * user-supplied pattern and the frame number. + * On successful return, finalName and tempName point to malloc()ed strings + * which must be freed externally. (On failure, returns nulls that + * don't need free()ing.) + * + * @param finalName pointer receives an + * @param pattern sprintf pattern with %d to be replaced by frame + * @param frame for timelapse, the frame number + * @return Returns a MMAL_STATUS_T giving result of operation +*/ + +MMAL_STATUS_T create_filenames(char** finalName, char** tempName, char * pattern, int frame) +{ + *finalName = NULL; + *tempName = NULL; + if (0 > asprintf(finalName, pattern, frame) || + 0 > asprintf(tempName, "%s~", *finalName)) + { + if (*finalName != NULL) + { + free(*finalName); + } + return MMAL_ENOMEM; // It may be some other error, but it is not worth getting it right + } + return MMAL_SUCCESS; +} + +/** + * Checks if specified port is valid and enabled, then disables it + * + * @param port Pointer the port + * + */ +static void check_disable_port(MMAL_PORT_T *port) +{ + if (port && port->is_enabled) + mmal_port_disable(port); +} + +/** + * Handler for sigint signals + * + * @param signal_number ID of incoming signal. + * + */ +static void signal_handler(int signal_number) +{ + if (signal_number == SIGUSR1 || signal_number == SIGUSR2) + { + // Handle but ignore - prevents us dropping out if started in none-signal mode + // and someone sends us the USR1 or USR2 signal anyway + } + else + { + // Going to abort on all other signals + vcos_log_error("Aborting program\n"); + exit(130); + } +} + + +/** + * Function to wait in various ways (depending on settings) for the next frame + * + * @param state Pointer to the state data + * @param [in][out] frame The last frame number, adjusted to next frame number on output + * @return !0 if to continue, 0 if reached end of run + */ +static int wait_for_next_frame(RASPISTILL_STATE *state, int *frame) +{ + static int64_t complete_time = -1; + int keep_running = 1; + + int64_t current_time = vcos_getmicrosecs64()/1000; + + if (complete_time == -1) + complete_time = current_time + state->timeout; + + // if we have run out of time, flag we need to exit + // If timeout = 0 then always continue + if (current_time >= complete_time && state->timeout != 0) + keep_running = 0; + + switch (state->frameNextMethod) + { + case FRAME_NEXT_SINGLE : + // simple timeout for a single capture + vcos_sleep(state->timeout); + return 0; + + case FRAME_NEXT_FOREVER : + { + *frame+=1; + + // Have a sleep so we don't hog the CPU. + vcos_sleep(10000); + + // Run forever so never indicate end of loop + return 1; + } + + case FRAME_NEXT_TIMELAPSE : + { + static int64_t next_frame_ms = -1; + + // Always need to increment by at least one, may add a skip later + *frame += 1; + + if (next_frame_ms == -1) + { + vcos_sleep(state->timelapse); + + // Update our current time after the sleep + current_time = vcos_getmicrosecs64()/1000; + + // Set our initial 'next frame time' + next_frame_ms = current_time + state->timelapse; + } + else + { + int64_t this_delay_ms = next_frame_ms - current_time; + + if (this_delay_ms < 0) + { + // We are already past the next exposure time + if (-this_delay_ms < state->timelapse/2) + { + // Less than a half frame late, take a frame and hope to catch up next time + next_frame_ms += state->timelapse; + vcos_log_error("Frame %d is %d ms late", *frame, (int)(-this_delay_ms)); + } + else + { + int nskip = 1 + (-this_delay_ms)/state->timelapse; + vcos_log_error("Skipping frame %d to restart at frame %d", *frame, *frame+nskip); + *frame += nskip; + this_delay_ms += nskip * state->timelapse; + vcos_sleep(this_delay_ms); + next_frame_ms += (nskip + 1) * state->timelapse; + } + } + else + { + vcos_sleep(this_delay_ms); + next_frame_ms += state->timelapse; + } + } + + return keep_running; + } + + case FRAME_NEXT_KEYPRESS : + { + int ch; + + if (state->verbose) + fprintf(stderr, "Press Enter to capture, X then ENTER to exit\n"); + + ch = getchar(); + *frame+=1; + if (ch == 'x' || ch == 'X') + return 0; + else + { + return keep_running; + } + } + + case FRAME_NEXT_IMMEDIATELY : + { + // Not waiting, just go to next frame. + // Actually, we do need a slight delay here otherwise exposure goes + // badly wrong since we never allow it frames to work it out + // This could probably be tuned down. + // First frame has a much longer delay to ensure we get exposure to a steady state + if (*frame == 0) + vcos_sleep(1000); + else + vcos_sleep(30); + + *frame+=1; + + return keep_running; + } + + case FRAME_NEXT_GPIO : + { + // Intended for GPIO firing of a capture + return 0; + } + + case FRAME_NEXT_SIGNAL : + { + // Need to wait for a SIGUSR1 or SIGUSR2 signal + sigset_t waitset; + int sig; + int result = 0; + + sigemptyset( &waitset ); + sigaddset( &waitset, SIGUSR1 ); + sigaddset( &waitset, SIGUSR2 ); + + // We are multi threaded because we use mmal, so need to use the pthread + // variant of procmask to block until a SIGUSR1 or SIGUSR2 signal appears + pthread_sigmask( SIG_BLOCK, &waitset, NULL ); + + if (state->verbose) + { + fprintf(stderr, "Waiting for SIGUSR1 to initiate capture and continue or SIGUSR2 to capture and exit\n"); + } + + result = sigwait( &waitset, &sig ); + + if (result == 0) + { + if (sig == SIGUSR1) + { + if (state->verbose) + fprintf(stderr, "Received SIGUSR1\n"); + } + else if (sig == SIGUSR2) + { + if (state->verbose) + fprintf(stderr, "Received SIGUSR2\n"); + keep_running = 0; + } + } + else + { + if (state->verbose) + fprintf(stderr, "Bad signal received - error %d\n", errno); + } + + *frame+=1; + + return keep_running; + } + } // end of switch + + // Should have returned by now, but default to timeout + return keep_running; +} + +static void rename_file(RASPISTILL_STATE *state, FILE *output_file, + const char *final_filename, const char *use_filename, int frame) +{ + MMAL_STATUS_T status; + + fclose(output_file); + vcos_assert(use_filename != NULL && final_filename != NULL); + if (0 != rename(use_filename, final_filename)) + { + vcos_log_error("Could not rename temp file to: %s; %s", + final_filename,strerror(errno)); + } + if (state->linkname) + { + char *use_link; + char *final_link; + status = create_filenames(&final_link, &use_link, state->linkname, frame); + + // Create hard link if possible, symlink otherwise + if (status != MMAL_SUCCESS + || (0 != link(final_filename, use_link) + && 0 != symlink(final_filename, use_link)) + || 0 != rename(use_link, final_link)) + { + vcos_log_error("Could not link as filename: %s; %s", + state->linkname,strerror(errno)); + } + if (use_link) free(use_link); + if (final_link) free(final_link); + } +} + +/** + * main + */ +int main(int argc, const char **argv) +{ + // Our main data storage vessel.. + RASPISTILL_STATE state = {0}; + int exit_code = EX_OK; + + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *camera_still_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + MMAL_PORT_T *encoder_input_port = NULL; + MMAL_PORT_T *encoder_output_port = NULL; + + bcm_host_init(); + + // Register our application with the logging system + vcos_log_register("RaspiStill", VCOS_LOG_CATEGORY); + + signal(SIGINT, signal_handler); + + // Disable USR1 and USR2 for the moment - may be reenabled if go in to signal capture mode + signal(SIGUSR1, SIG_IGN); + signal(SIGUSR2, SIG_IGN); + + default_status(&state); + + // Do we have any parameters + if (argc == 1) + { + fprintf(stdout, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + display_valid_parameters(basename(argv[0])); + exit(EX_USAGE); + } + + // Parse the command line and put options in to our status structure + if (parse_cmdline(argc, argv, &state)) + { + exit(EX_USAGE); + } + + // Setup for sensor specific parameters + set_sensor_defaults(&state); + + if (state.verbose) + { + fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + dump_status(&state); + } + + if (state.useGL) + raspitex_init(&state.raspitex_state); + + // OK, we have a nice set of parameters. Now set up our components + // We have three components. Camera, Preview and encoder. + // Camera and encoder are different in stills/video, but preview + // is the same so handed off to a separate module + + if ((status = create_camera_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + exit_code = EX_SOFTWARE; + } + else if ((!state.useGL) && (status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create encode component", __func__); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else + { + PORT_USERDATA callback_data; + + if (state.verbose) + fprintf(stderr, "Starting component connection stage\n"); + + camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + encoder_input_port = state.encoder_component->input[0]; + encoder_output_port = state.encoder_component->output[0]; + + if (! state.useGL) + { + if (state.verbose) + fprintf(stderr, "Connecting camera preview port to video render.\n"); + + // Note we are lucky that the preview and null sink components use the same input port + // so we can simple do this without conditionals + preview_input_port = state.preview_parameters.preview_component->input[0]; + + // Connect camera to preview (which might be a null_sink if no preview required) + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + } + + if (status == MMAL_SUCCESS) + { + VCOS_STATUS_T vcos_status; + + if (state.verbose) + fprintf(stderr, "Connecting camera stills port to encoder input port\n"); + + // Now connect the camera to the encoder + status = connect_ports(camera_still_port, encoder_input_port, &state.encoder_connection); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); + goto error; + } + + // Set up our userdata - this is passed though to the callback where we need the information. + // Null until we open our filename + callback_data.file_handle = NULL; + callback_data.pstate = &state; + vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "RaspiStill-sem", 0); + + vcos_assert(vcos_status == VCOS_SUCCESS); + + /* If GL preview is requested then start the GL threads */ + if (state.useGL && (raspitex_start(&state.raspitex_state) != 0)) + goto error; + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup encoder output"); + goto error; + } + + if (state.demoMode) + { + // Run for the user specific time.. + int num_iterations = state.timeout / state.demoInterval; + int i; + for (i=0;itm_mon+1; + frame *= 100; + frame += timeinfo->tm_mday; + frame *= 100; + frame += timeinfo->tm_hour; + frame *= 100; + frame += timeinfo->tm_min; + frame *= 100; + frame += timeinfo->tm_sec; + } + if (state.timestamp) + { + frame = (int)time(NULL); + } + + // Open the file + if (state.filename) + { + if (state.filename[0] == '-') + { + output_file = stdout; + + // Ensure we don't upset the output stream with diagnostics/info + state.verbose = 0; + } + else + { + vcos_assert(use_filename == NULL && final_filename == NULL); + status = create_filenames(&final_filename, &use_filename, state.filename, frame); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create filenames"); + goto error; + } + + if (state.verbose) + fprintf(stderr, "Opening output file %s\n", final_filename); + // Technically it is opening the temp~ filename which will be ranamed to the final filename + + output_file = fopen(use_filename, "wb"); + + if (!output_file) + { + // Notify user, carry on but discarding encoded output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, use_filename); + } + } + + callback_data.file_handle = output_file; + } + + // We only capture if a filename was specified and it opened + if (state.useGL && state.glCapture && output_file) + { + /* Save the next GL framebuffer as the next camera still */ + int rc = raspitex_capture(&state.raspitex_state, output_file); + if (rc != 0) + vcos_log_error("Failed to capture GL preview"); + rename_file(&state, output_file, final_filename, use_filename, frame); + } + else if (output_file) + { + int num, q; + + // Must do this before the encoder output port is enabled since + // once enabled no further exif data is accepted + if ( state.enableExifTags ) + { + add_exif_tags(&state); + } + else + { + mmal_port_parameter_set_boolean( + state.encoder_component->output[0], MMAL_PARAMETER_EXIF_DISABLE, 1); + } + + // Same with raw, apparently need to set it for each capture, whilst port + // is not enabled + if (state.wantRAW) + { + if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_ENABLE_RAW_CAPTURE, 1) != MMAL_SUCCESS) + { + vcos_log_error("RAW was requested, but failed to enable"); + } + } + + // There is a possibility that shutter needs to be set each loop. + if (mmal_status_to_int(mmal_port_parameter_set_uint32(state.camera_component->control, MMAL_PARAMETER_SHUTTER_SPEED, state.camera_parameters.shutter_speed)) != MMAL_SUCCESS) + vcos_log_error("Unable to set shutter speed"); + + + // Enable the encoder output port + encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling encoder output port\n"); + + // Enable the encoder output port and tell it its callback function + status = mmal_port_enable(encoder_output_port, encoder_buffer_callback); + + // Send all the buffers to the encoder output port + num = mmal_queue_length(state.encoder_pool->queue); + + for (q=0;qqueue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(encoder_output_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); + } + + if (state.burstCaptureMode && frame==1) + { + mmal_port_parameter_set_boolean(state.camera_component->control, MMAL_PARAMETER_CAMERA_BURST_CAPTURE, 1); + } + + if(state.camera_parameters.enable_annotate) + raspicamcontrol_set_annotate(state.camera_component, state.camera_parameters.enable_annotate, + state.camera_parameters.annotate_string, + state.camera_parameters.annotate_text_size, + state.camera_parameters.annotate_text_colour, + state.camera_parameters.annotate_bg_colour); + + if (state.verbose) + fprintf(stderr, "Starting capture %d\n", frame); + + if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to start capture", __func__); + } + else + { + // Wait for capture to complete + // For some reason using vcos_semaphore_wait_timeout sometimes returns immediately with bad parameter error + // even though it appears to be all correct, so reverting to untimed one until figure out why its erratic + vcos_semaphore_wait(&callback_data.complete_semaphore); + if (state.verbose) + fprintf(stderr, "Finished capture %d\n", frame); + } + + // Ensure we don't die if get callback with no open file + callback_data.file_handle = NULL; + + if (output_file != stdout) + { + rename_file(&state, output_file, final_filename, use_filename, frame); + } + else + { + fflush(output_file); + } + // Disable encoder output port + status = mmal_port_disable(encoder_output_port); + } + + if (use_filename) + { + free(use_filename); + use_filename = NULL; + } + if (final_filename) + { + free(final_filename); + final_filename = NULL; + } + } // end for (frame) + + vcos_semaphore_delete(&callback_data.complete_semaphore); + } + } + else + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + } + +error: + + mmal_status_to_int(status); + + if (state.verbose) + fprintf(stderr, "Closing down\n"); + + if (state.useGL) + { + raspitex_stop(&state.raspitex_state); + raspitex_destroy(&state.raspitex_state); + } + + // Disable all our ports that are not handled by connections + check_disable_port(camera_video_port); + check_disable_port(encoder_output_port); + + if (state.preview_connection) + mmal_connection_destroy(state.preview_connection); + + if (state.encoder_connection) + mmal_connection_destroy(state.encoder_connection); + + + /* Disable components */ + if (state.encoder_component) + mmal_component_disable(state.encoder_component); + + if (state.preview_parameters.preview_component) + mmal_component_disable(state.preview_parameters.preview_component); + + if (state.camera_component) + mmal_component_disable(state.camera_component); + + destroy_encoder_component(&state); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + + if (state.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + } + + if (status != MMAL_SUCCESS) + raspicamcontrol_check_configuration(128); + + return exit_code; +} + + diff --git a/host_applications/linux/apps/raspicam/RaspiStillYUV.c b/host_applications/linux/apps/raspicam/RaspiStillYUV.c new file mode 100755 index 0000000..ac178a3 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiStillYUV.c @@ -0,0 +1,1472 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file RaspiStillYUV.c + * Command line program to capture a still frame and dump uncompressed it to file. + * Also optionally display a preview/viewfinder of current camera input. + * + * \date 4th March 2013 + * \Author: James Hughes + * + * Description + * + * 2 components are created; camera and preview. + * Camera component has three ports, preview, video and stills. + * Preview is connected using standard mmal connections, the stills output + * is written straight to the file in YUV 420 format via the requisite buffer + * callback. video port is not used + * + * We use the RaspiCamControl code to handle the specific camera settings. + * We use the RaspiPreview code to handle the generic preview + */ + +// We use some GNU extensions (basename) +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#define VERSION_STRING "v1.3.7" + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" + + +#include "RaspiCamControl.h" +#include "RaspiPreview.h" +#include "RaspiCLI.h" + +#include + +// Standard port setting for the camera component +#define MMAL_CAMERA_PREVIEW_PORT 0 +#define MMAL_CAMERA_VIDEO_PORT 1 +#define MMAL_CAMERA_CAPTURE_PORT 2 + + +// Stills format information +// 0 implies variable +#define STILLS_FRAME_RATE_NUM 0 +#define STILLS_FRAME_RATE_DEN 1 + +/// Video render needs at least 2 buffers. +#define VIDEO_OUTPUT_BUFFERS_NUM 3 + +/// Frame advance method +#define FRAME_NEXT_SINGLE 0 +#define FRAME_NEXT_TIMELAPSE 1 +#define FRAME_NEXT_KEYPRESS 2 +#define FRAME_NEXT_FOREVER 3 +#define FRAME_NEXT_GPIO 4 +#define FRAME_NEXT_SIGNAL 5 +#define FRAME_NEXT_IMMEDIATELY 6 + +int mmal_status_to_int(MMAL_STATUS_T status); +static void signal_handler(int signal_number); + +/** Structure containing all state information for the current run + */ +typedef struct +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + char *filename; /// filename of output file + char *linkname; /// filename of output file + int verbose; /// !0 if want detailed run information + int timelapse; /// Delay between each picture in timelapse mode. If 0, disable timelapse + int useRGB; /// Output RGB data rather than YUV + int fullResPreview; /// If set, the camera preview port runs at capture resolution. Reduces fps. + int frameNextMethod; /// Which method to use to advance to next frame + int settings; /// Request settings from the camera + int cameraNum; /// Camera number + int burstCaptureMode; /// Enable burst mode + int onlyLuma; /// Only output the luma / Y plane of the YUV data + + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component + MMAL_COMPONENT_T *null_sink_component; /// Pointer to the camera component + MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview + MMAL_POOL_T *camera_pool; /// Pointer to the pool of buffers used by camera stills port +} RASPISTILLYUV_STATE; + + +/** Struct used to pass information in camera still port userdata to callback + */ +typedef struct +{ + FILE *file_handle; /// File handle to write buffer data to. + VCOS_SEMAPHORE_T complete_semaphore; /// semaphore which is posted when we reach end of frame (indicates end of capture or fault) + RASPISTILLYUV_STATE *pstate; /// pointer to our state in case required in callback +} PORT_USERDATA; + +static void display_valid_parameters(char *app_name); + +/// Comamnd ID's and Structure defining our command line options +#define CommandHelp 0 +#define CommandWidth 1 +#define CommandHeight 2 +#define CommandOutput 3 +#define CommandVerbose 4 +#define CommandTimeout 5 +#define CommandTimelapse 6 +#define CommandUseRGB 7 +#define CommandCamSelect 8 +#define CommandFullResPreview 9 +#define CommandLink 10 +#define CommandKeypress 11 +#define CommandSignal 12 +#define CommandSettings 13 +#define CommandBurstMode 14 +#define CommandOnlyLuma 15 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width ", 1 }, + { CommandHeight, "-height", "h", "Set image height ", 1 }, + { CommandOutput, "-output", "o", "Output filename . If not specifed, no image is saved", 1 }, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) before takes picture and shuts down. If not specified set to 5s", 1 }, + { CommandTimelapse,"-timelapse", "tl", "Timelapse mode. Takes a picture every ms", 1}, + { CommandUseRGB, "-rgb", "rgb","Save as RGB data rather than YUV", 0}, + { CommandCamSelect,"-camselect", "cs", "Select camera . Default 0", 1 }, + { CommandFullResPreview,"-fullpreview","fp", "Run the preview using the still capture resolution (may reduce preview fps)", 0}, + { CommandLink, "-latest", "l", "Link latest complete image to filename ", 1}, + { CommandKeypress,"-keypress", "k", "Wait between captures for a ENTER, X then ENTER to exit", 0}, + { CommandSignal, "-signal", "s", "Wait between captures for a SIGUSR1 from another process", 0}, + { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0}, + { CommandBurstMode, "-burst", "bm", "Enable 'burst capture mode'", 0}, + { CommandOnlyLuma, "-luma", "y", "Only output the luma / Y of the YUV data'", 0}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +static struct +{ + char *description; + int nextFrameMethod; +} next_frame_description[] = +{ + {"Single capture", FRAME_NEXT_SINGLE}, + {"Capture on timelapse", FRAME_NEXT_TIMELAPSE}, + {"Capture on keypress", FRAME_NEXT_KEYPRESS}, + {"Run forever", FRAME_NEXT_FOREVER}, + {"Capture on GPIO", FRAME_NEXT_GPIO}, + {"Capture on signal", FRAME_NEXT_SIGNAL}, +}; + +static int next_frame_description_size = sizeof(next_frame_description) / sizeof(next_frame_description[0]); + +/** + * Assign a default set of parameters to the state passed in + * + * @param state Pointer to state structure to assign defaults to + */ +static void default_status(RASPISTILLYUV_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + // Default everything to zero + memset(state, 0, sizeof(RASPISTILLYUV_STATE)); + + // Now set anything non-zero + state->timeout = 5000; // 5s delay before take image + state->width = 2592; + state->height = 1944; + state->timelapse = 0; + state->filename = NULL; + state->linkname = NULL; + state->verbose = 0; + state->fullResPreview = 0; + state->frameNextMethod = FRAME_NEXT_SINGLE; + state->settings = 0; + state->burstCaptureMode=0; + state->onlyLuma = 0; + + // Setup preview window defaults + raspipreview_set_defaults(&state->preview_parameters); + + // Set up the camera_parameters to default + raspicamcontrol_set_defaults(&state->camera_parameters); + + // Set default camera + state->cameraNum = 0; +} + +/** + * Dump image state parameters to stderr. Used for debugging + * + * @param state Pointer to state structure to assign defaults to + */ +static void dump_status(RASPISTILLYUV_STATE *state) +{ + int i; + if (!state) + { + vcos_assert(0); + return; + } + + fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, + state->height, state->filename); + fprintf(stderr, "Time delay %d, Timelapse %d\n", state->timeout, state->timelapse); + fprintf(stderr, "Link to latest frame enabled "); + if (state->linkname) + { + fprintf(stderr, " yes, -> %s\n", state->linkname); + } + else + { + fprintf(stderr, " no\n"); + } + fprintf(stderr, "Full resolution preview %s\n", state->fullResPreview ? "Yes": "No"); + + fprintf(stderr, "Capture method : "); + for (i=0;iframeNextMethod == next_frame_description[i].nextFrameMethod) + fprintf(stderr, "%s", next_frame_description[i].description); + } + fprintf(stderr, "\n\n"); + + raspipreview_dump_parameters(&state->preview_parameters); + raspicamcontrol_dump_parameters(&state->camera_parameters); +} + +/** + * Parse the incoming command line and put resulting parameters in to the state + * + * @param argc Number of arguments in command line + * @param argv Array of pointers to strings from command line + * @param state Pointer to state structure to assign any discovered parameters to + * @return non-0 if failed for some reason, 0 otherwise + */ +static int parse_cmdline(int argc, const char **argv, RASPISTILLYUV_STATE *state) +{ + // Parse the command line arguments. + // We are looking for -- or - + + int valid = 1; // set 0 if we have a bad parameter + int i; + + for (i = 1; i < argc && valid; i++) + { + int command_id, num_parameters; + + if (!argv[i]) + continue; + + if (argv[i][0] != '-') + { + valid = 0; + continue; + } + + // Assume parameter is valid until proven otherwise + valid = 1; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters); + + // If we found a command but are missing a parameter, continue (and we will drop out of the loop) + if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) ) + continue; + + // We are now dealing with a command line option + switch (command_id) + { + case CommandHelp: + display_valid_parameters(basename(argv[0])); + return -1; + + case CommandWidth: // Width > 0 + if (sscanf(argv[i + 1], "%u", &state->width) != 1) + valid = 0; + else + i++; + break; + + case CommandHeight: // Height > 0 + if (sscanf(argv[i + 1], "%u", &state->height) != 1) + valid = 0; + else + i++; + break; + + case CommandOutput: // output filename + { + int len = strlen(argv[i + 1]); + if (len) + { + state->filename = malloc(len + 10); // leave enough space for any timelapse generated changes to filename + vcos_assert(state->filename); + if (state->filename) + strncpy(state->filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandLink : + { + int len = strlen(argv[i+1]); + if (len) + { + state->linkname = malloc(len + 10); + vcos_assert(state->linkname); + if (state->linkname) + strncpy(state->linkname, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + + } + + case CommandVerbose: // display lots of data during run + state->verbose = 1; + break; + + case CommandTimeout: // Time to run viewfinder for before taking picture, in seconds + { + if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) + { + // Ensure that if previously selected CommandKeypress we don't overwrite it + if (state->timeout == 0 && state->frameNextMethod == FRAME_NEXT_SINGLE) + state->frameNextMethod = FRAME_NEXT_FOREVER; + + i++; + } + else + valid = 0; + break; + } + + case CommandTimelapse: + if (sscanf(argv[i + 1], "%u", &state->timelapse) != 1) + valid = 0; + else + { + if (state->timelapse) + state->frameNextMethod = FRAME_NEXT_TIMELAPSE; + else + state->frameNextMethod = FRAME_NEXT_IMMEDIATELY; + + + i++; + } + break; + + case CommandUseRGB: // display lots of data during run + if (state->onlyLuma) + { + fprintf(stderr, "--luma and --rgb are mutually exclusive\n"); + valid = 0; + } + state->useRGB = 1; + break; + + case CommandCamSelect: //Select camera input port + { + if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandFullResPreview: + state->fullResPreview = 1; + break; + + case CommandKeypress: // Set keypress between capture mode + state->frameNextMethod = FRAME_NEXT_KEYPRESS; + break; + + case CommandSignal: // Set SIGUSR1 between capture mode + state->frameNextMethod = FRAME_NEXT_SIGNAL; + // Reenable the signal + signal(SIGUSR1, signal_handler); + break; + + case CommandSettings: + state->settings = 1; + break; + + case CommandBurstMode: + state->burstCaptureMode=1; + break; + + case CommandOnlyLuma: + if (state->useRGB) + { + fprintf(stderr, "--luma and --rgb are mutually exclusive\n"); + valid = 0; + } + state->onlyLuma = 1; + break; + + default: + { + // Try parsing for any image specific parameters + // result indicates how many parameters were used up, 0,1,2 + // but we adjust by -1 as we have used one already + const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL; + + int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg)); + + // Still unused, try preview options + if (!parms_used) + parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + + + // If no parms were used, this must be a bad parameters + if (!parms_used) + valid = 0; + else + i += parms_used - 1; + + break; + } + } + } + + if (!valid) + { + fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]); + return 1; + } + + return 0; +} + +/** + * Display usage information for the application to stdout + * + * @param app_name String to display as the application name + * + */ +static void display_valid_parameters(char *app_name) +{ + fprintf(stdout, "Runs camera for specific time, and take uncompressed YUV capture at end if requested\n\n"); + fprintf(stdout, "usage: %s [options]\n\n", app_name); + + fprintf(stdout, "Image parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + // Help for preview options + raspipreview_display_help(); + + // Now display any help information from the camcontrol code + raspicamcontrol_display_help(); + + fprintf(stdout, "\n"); + + return; +} + +/** + * buffer header callback function for camera control + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + fprintf(stderr, "Camera control callback cmd=0x%08x", buffer->cmd); + + if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED) + { + MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data; + switch (param->hdr.id) { + case MMAL_PARAMETER_CAMERA_SETTINGS: + { + MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param; + vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u", + settings->exposure, + settings->analog_gain.num, settings->analog_gain.den, + settings->digital_gain.num, settings->digital_gain.den); + vcos_log_error("AWB R=%u/%u, B=%u/%u", + settings->awb_red_gain.num, settings->awb_red_gain.den, + settings->awb_blue_gain.num, settings->awb_blue_gain.den + ); + } + break; + } + } + else if (buffer->cmd == MMAL_EVENT_ERROR) + { + vcos_log_error("No data received from sensor. Check all connections, including the Sunny one on the camera board"); + } + else + { + vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); + } + + mmal_buffer_header_release(buffer); +} + +/** + * buffer header callback function for camera output port + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + int complete = 0; + // We pass our file handle and other stuff in via the userdata field. + + + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = 0; + int bytes_to_write = buffer->length; + + if (pData->pstate->onlyLuma) + bytes_to_write = vcos_min(buffer->length, port->format->es->video.width * port->format->es->video.height); + + if (bytes_to_write && pData->file_handle) + { + mmal_buffer_header_mem_lock(buffer); + + bytes_written = fwrite(buffer->data, 1, bytes_to_write, pData->file_handle); + + mmal_buffer_header_mem_unlock(buffer); + } + + // We need to check we wrote what we wanted - it's possible we have run out of storage. + if (buffer->length && bytes_written != bytes_to_write) + { + vcos_log_error("Unable to write buffer to file - aborting %d vs %d", bytes_written, bytes_to_write); + complete = 1; + } + + // Check end of frame or error + if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED)) + complete = 1; + } + else + { + vcos_log_error("Received a camera still buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status; + MMAL_BUFFER_HEADER_T *new_buffer = mmal_queue_get(pData->pstate->camera_pool->queue); + + // and back to the port from there. + if (new_buffer) + { + status = mmal_port_send_buffer(port, new_buffer); + } + + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return the buffer to the camera still port"); + } + + if (complete) + { + vcos_semaphore_post(&(pData->complete_semaphore)); + } +} + + +/** + * Create the camera component, set up its ports + * + * @param state Pointer to state control struct + * + * @return 0 if failed, pointer to component if successful + * + */ +static MMAL_STATUS_T create_camera_component(RASPISTILLYUV_STATE *state) +{ + MMAL_COMPONENT_T *camera = 0; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create camera component"); + goto error; + } + + MMAL_PARAMETER_INT32_T camera_num = + {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, state->cameraNum}; + + status = mmal_port_parameter_set(camera->control, &camera_num.hdr); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not select camera : error %d", status); + goto error; + } + + if (!camera->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera doesn't have output ports"); + goto error; + } + + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + if (state->settings) + { + MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request = + {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)}, + MMAL_PARAMETER_CAMERA_SETTINGS, 1}; + + status = mmal_port_parameter_set(camera->control, &change_event_request.hdr); + if ( status != MMAL_SUCCESS ) + { + vcos_log_error("No camera settings events"); + } + } + + // Enable the camera, and tell it its control callback function + status = mmal_port_enable(camera->control, camera_control_callback); + + if (status != MMAL_SUCCESS ) + { + vcos_log_error("Unable to enable control port : error %d", status); + goto error; + } + + // set up the camera configuration + { + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = + { + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->width, + .max_stills_h = state->height, + .stills_yuv422 = 0, + .one_shot_stills = 1, + .max_preview_video_w = state->preview_parameters.previewWindow.width, + .max_preview_video_h = state->preview_parameters.previewWindow.height, + .num_preview_video_frames = 3, + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC + }; + + if (state->fullResPreview) + { + cam_config.max_preview_video_w = state->width; + cam_config.max_preview_video_h = state->height; + } + + mmal_port_parameter_set(camera->control, &cam_config.hdr); + } + + raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + + // Now set up the port formats + + format = preview_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 166, 1000 }, {999, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + if (state->fullResPreview) + { + // In this mode we are forcing the preview to be generated from the full capture resolution. + // This runs at a max of 15fps with the OV5647 sensor. + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = FULL_RES_PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = FULL_RES_PREVIEW_FRAME_RATE_DEN; + } + else + { + // Use a full FOV 4:3 mode + format->es->video.width = VCOS_ALIGN_UP(state->preview_parameters.previewWindow.width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->preview_parameters.previewWindow.height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->preview_parameters.previewWindow.width; + format->es->video.crop.height = state->preview_parameters.previewWindow.height; + format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; + } + + status = mmal_port_format_commit(preview_port); + + if (status != MMAL_SUCCESS ) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto error; + } + + // Set the same format on the video port (which we don't use here) + mmal_format_full_copy(video_port->format, format); + status = mmal_port_format_commit(video_port); + + if (status != MMAL_SUCCESS ) + { + vcos_log_error("camera video format couldn't be set"); + goto error; + } + + // Ensure there are enough buffers to avoid dropping frames + if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + format = still_port->format; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(still_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 167, 1000 }, {999, 1000}}; + mmal_port_parameter_set(still_port, &fps_range.hdr); + } + // Set our stills format on the stills port + if (state->useRGB) + { + format->encoding = mmal_util_rgb_order_fixed(still_port) ? MMAL_ENCODING_RGB24 : MMAL_ENCODING_BGR24; + format->encoding_variant = 0; //Irrelevant when not in opaque mode + } + else + { + format->encoding = MMAL_ENCODING_I420; + format->encoding_variant = MMAL_ENCODING_I420; + } + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = STILLS_FRAME_RATE_NUM; + format->es->video.frame_rate.den = STILLS_FRAME_RATE_DEN; + + if (still_port->buffer_size < still_port->buffer_size_min) + still_port->buffer_size = still_port->buffer_size_min; + + still_port->buffer_num = still_port->buffer_num_recommended; + + status = mmal_port_parameter_set_boolean(video_port, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to select zero copy"); + goto error; + } + + status = mmal_port_format_commit(still_port); + + if (status != MMAL_SUCCESS ) + { + vcos_log_error("camera still format couldn't be set"); + goto error; + } + + /* Enable component */ + status = mmal_component_enable(camera); + + if (status != MMAL_SUCCESS ) + { + vcos_log_error("camera component couldn't be enabled"); + goto error; + } + + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(still_port, still_port->buffer_num, still_port->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for camera still port %s", still_port->name); + } + + state->camera_pool = pool; + state->camera_component = camera; + + if (state->verbose) + fprintf(stderr, "Camera component done\n"); + + return status; + +error: + + if (camera) + mmal_component_destroy(camera); + + return status; +} + +/** + * Destroy the camera component + * + * @param state Pointer to state control struct + * + */ +static void destroy_camera_component(RASPISTILLYUV_STATE *state) +{ + if (state->camera_component) + { + mmal_component_destroy(state->camera_component); + state->camera_component = NULL; + } +} + +/** + * Connect two specific ports together + * + * @param output_port Pointer the output port + * @param input_port Pointer the input port + * @param Pointer to a mmal connection pointer, reassigned if function successful + * @return Returns a MMAL_STATUS_T giving result of operation + * + */ +static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) +{ + MMAL_STATUS_T status; + + status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); + + if (status == MMAL_SUCCESS) + { + status = mmal_connection_enable(*connection); + if (status != MMAL_SUCCESS) + mmal_connection_destroy(*connection); + } + + return status; +} + +/** + * Allocates and generates a filename based on the + * user-supplied pattern and the frame number. + * On successful return, finalName and tempName point to malloc()ed strings + * which must be freed externally. (On failure, returns nulls that + * don't need free()ing.) + * + * @param finalName pointer receives an + * @param pattern sprintf pattern with %d to be replaced by frame + * @param frame for timelapse, the frame number + * @return Returns a MMAL_STATUS_T giving result of operation +*/ + +MMAL_STATUS_T create_filenames(char** finalName, char** tempName, char * pattern, int frame) +{ + *finalName = NULL; + *tempName = NULL; + if (0 > asprintf(finalName, pattern, frame) || + 0 > asprintf(tempName, "%s~", *finalName)) + { + if (*finalName != NULL) + { + free(*finalName); + } + return MMAL_ENOMEM; // It may be some other error, but it is not worth getting it right + } + return MMAL_SUCCESS; +} + +/** + * Checks if specified port is valid and enabled, then disables it + * + * @param port Pointer the port + * + */ +static void check_disable_port(MMAL_PORT_T *port) +{ + if (port && port->is_enabled) + mmal_port_disable(port); +} + +/** + * Handler for sigint signals + * + * @param signal_number ID of incoming signal. + * + */ +static void signal_handler(int signal_number) +{ + if (signal_number == SIGUSR1) + { + // Handle but ignore - prevents us dropping out if started in none-signal mode + // and someone sends us the USR1 signal anyway + } + else + { + // Going to abort on all other signals + vcos_log_error("Aborting program\n"); + exit(130); + } +} + +/** + * Function to wait in various ways (depending on settings) for the next frame + * + * @param state Pointer to the state data + * @param [in][out] frame The last frame number, adjusted to next frame number on output + * @return !0 if to continue, 0 if reached end of run + */ +static int wait_for_next_frame(RASPISTILLYUV_STATE *state, int *frame) +{ + static int64_t complete_time = -1; + int keep_running = 1; + + int64_t current_time = vcos_getmicrosecs64()/1000; + + if (complete_time == -1) + complete_time = current_time + state->timeout; + + // if we have run out of time, flag we need to exit + // If timeout = 0 then always continue + if (current_time >= complete_time && state->timeout != 0) + keep_running = 0; + + switch (state->frameNextMethod) + { + case FRAME_NEXT_SINGLE : + // simple timeout for a single capture + vcos_sleep(state->timeout); + return 0; + + case FRAME_NEXT_FOREVER : + { + *frame+=1; + + // Have a sleep so we don't hog the CPU. + vcos_sleep(10000); + + // Run forever so never indicate end of loop + return 1; + } + + case FRAME_NEXT_TIMELAPSE : + { + static int64_t next_frame_ms = -1; + + // Always need to increment by at least one, may add a skip later + *frame += 1; + + if (next_frame_ms == -1) + { + vcos_sleep(state->timelapse); + + // Update our current time after the sleep + current_time = vcos_getmicrosecs64()/1000; + + // Set our initial 'next frame time' + next_frame_ms = current_time + state->timelapse; + } + else + { + int64_t this_delay_ms = next_frame_ms - current_time; + + if (this_delay_ms < 0) + { + // We are already past the next exposure time + if (-this_delay_ms < -state->timelapse/2) + { + // Less than a half frame late, take a frame and hope to catch up next time + next_frame_ms += state->timelapse; + vcos_log_error("Frame %d is %d ms late", *frame, (int)(-this_delay_ms)); + } + else + { + int nskip = 1 + (-this_delay_ms)/state->timelapse; + vcos_log_error("Skipping frame %d to restart at frame %d", *frame, *frame+nskip); + *frame += nskip; + this_delay_ms += nskip * state->timelapse; + vcos_sleep(this_delay_ms); + next_frame_ms += (nskip + 1) * state->timelapse; + } + } + else + { + vcos_sleep(this_delay_ms); + next_frame_ms += state->timelapse; + } + } + + return keep_running; + } + + case FRAME_NEXT_KEYPRESS : + { + int ch; + + if (state->verbose) + fprintf(stderr, "Press Enter to capture, X then ENTER to exit\n"); + + ch = getchar(); + *frame+=1; + if (ch == 'x' || ch == 'X') + return 0; + else + { + return keep_running; + } + } + + case FRAME_NEXT_IMMEDIATELY : + { + // Not waiting, just go to next frame. + // Actually, we do need a slight delay here otherwise exposure goes + // badly wrong since we never allow it frames to work it out + // This could probably be tuned down. + // First frame has a much longer delay to ensure we get exposure to a steady state + if (*frame == 0) + vcos_sleep(1000); + else + vcos_sleep(30); + + *frame+=1; + + return keep_running; + } + + case FRAME_NEXT_GPIO : + { + // Intended for GPIO firing of a capture + return 0; + } + + case FRAME_NEXT_SIGNAL : + { + // Need to wait for a SIGUSR1 signal + sigset_t waitset; + int sig; + int result = 0; + + sigemptyset( &waitset ); + sigaddset( &waitset, SIGUSR1 ); + + // We are multi threaded because we use mmal, so need to use the pthread + // variant of procmask to block SIGUSR1 so we can wait on it. + pthread_sigmask( SIG_BLOCK, &waitset, NULL ); + + if (state->verbose) + { + fprintf(stderr, "Waiting for SIGUSR1 to initiate capture\n"); + } + + result = sigwait( &waitset, &sig ); + + if (state->verbose) + { + if( result == 0) + { + fprintf(stderr, "Received SIGUSR1\n"); + } + else + { + fprintf(stderr, "Bad signal received - error %d\n", errno); + } + } + + *frame+=1; + + return keep_running; + } + } // end of switch + + // Should have returned by now, but default to timeout + return keep_running; +} + +static void rename_file(RASPISTILLYUV_STATE *state, FILE *output_file, + const char *final_filename, const char *use_filename, int frame) +{ + MMAL_STATUS_T status; + + fclose(output_file); + vcos_assert(use_filename != NULL && final_filename != NULL); + if (0 != rename(use_filename, final_filename)) + { + vcos_log_error("Could not rename temp file to: %s; %s", + final_filename,strerror(errno)); + } + if (state->linkname) + { + char *use_link; + char *final_link; + status = create_filenames(&final_link, &use_link, state->linkname, frame); + + // Create hard link if possible, symlink otherwise + if (status != MMAL_SUCCESS + || (0 != link(final_filename, use_link) + && 0 != symlink(final_filename, use_link)) + || 0 != rename(use_link, final_link)) + { + vcos_log_error("Could not link as filename: %s; %s", + state->linkname,strerror(errno)); + } + if (use_link) free(use_link); + if (final_link) free(final_link); + } +} + +/** + * main + */ +int main(int argc, const char **argv) +{ + // Our main data storage vessel.. + RASPISTILLYUV_STATE state; + int exit_code = EX_OK; + + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *camera_still_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + FILE *output_file = NULL; + + bcm_host_init(); + + // Register our application with the logging system + vcos_log_register("RaspiStill", VCOS_LOG_CATEGORY); + + signal(SIGINT, signal_handler); + + // Disable USR1 for the moment - may be reenabled if go in to signal capture mode + signal(SIGUSR1, SIG_IGN); + + default_status(&state); + + // Do we have any parameters + if (argc == 1) + { + fprintf(stdout, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + display_valid_parameters(basename(argv[0])); + exit(EX_USAGE); + } + + default_status(&state); + + // Parse the command line and put options in to our status structure + if (parse_cmdline(argc, argv, &state)) + { + status = -1; + exit(EX_USAGE); + } + + if (state.verbose) + { + fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + dump_status(&state); + } + + // OK, we have a nice set of parameters. Now set up our components + // We have two components. Camera and Preview + // Camera is different in stills/video, but preview + // is the same so handed off to a separate module + + if ((status = create_camera_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + exit_code = EX_SOFTWARE; + } + else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else + { + PORT_USERDATA callback_data; + + if (state.verbose) + fprintf(stderr, "Starting component connection stage\n"); + + camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + + // Note we are lucky that the preview and null sink components use the same input port + // so we can simple do this without conditionals + preview_input_port = state.preview_parameters.preview_component->input[0]; + + // Connect camera to preview (which might be a null_sink if no preview required) + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + + if (status == MMAL_SUCCESS) + { + VCOS_STATUS_T vcos_status; + + // Set up our userdata - this is passed though to the callback where we need the information. + // Null until we open our filename + callback_data.file_handle = NULL; + callback_data.pstate = &state; + + vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "RaspiStill-sem", 0); + vcos_assert(vcos_status == VCOS_SUCCESS); + + camera_still_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling camera still output port\n"); + + // Enable the camera still output port and tell it its callback function + status = mmal_port_enable(camera_still_port, camera_buffer_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup camera output"); + goto error; + } + + if (state.verbose) + fprintf(stderr, "Starting video preview\n"); + + + int frame, keep_looping = 1; + FILE *output_file = NULL; + char *use_filename = NULL; // Temporary filename while image being written + char *final_filename = NULL; // Name that file gets once writing complete + + frame = 0; + + while (keep_looping) + { + keep_looping = wait_for_next_frame(&state, &frame); + + // Open the file + if (state.filename) + { + if (state.filename[0] == '-') + { + output_file = stdout; + + // Ensure we don't upset the output stream with diagnostics/info + state.verbose = 0; + } + else + { + vcos_assert(use_filename == NULL && final_filename == NULL); + status = create_filenames(&final_filename, &use_filename, state.filename, frame); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create filenames"); + goto error; + } + + if (state.verbose) + fprintf(stderr, "Opening output file %s\n", final_filename); + // Technically it is opening the temp~ filename which will be ranamed to the final filename + + output_file = fopen(use_filename, "wb"); + + if (!output_file) + { + // Notify user, carry on but discarding encoded output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, use_filename); + } + } + + callback_data.file_handle = output_file; + } + + if (output_file) + { + int num, q; + + // There is a possibility that shutter needs to be set each loop. + if (mmal_status_to_int(mmal_port_parameter_set_uint32(state.camera_component->control, MMAL_PARAMETER_SHUTTER_SPEED, state.camera_parameters.shutter_speed) != MMAL_SUCCESS)) + vcos_log_error("Unable to set shutter speed"); + + + // Send all the buffers to the camera output port + num = mmal_queue_length(state.camera_pool->queue); + + for (q=0;qqueue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(camera_still_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to camera output port (%d)", q); + } + + if (state.burstCaptureMode && frame==1) + { + mmal_port_parameter_set_boolean(state.camera_component->control, MMAL_PARAMETER_CAMERA_BURST_CAPTURE, 1); + } + + if (state.verbose) + fprintf(stderr, "Starting capture %d\n", frame); + + if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to start capture", __func__); + } + else + { + // Wait for capture to complete + // For some reason using vcos_semaphore_wait_timeout sometimes returns immediately with bad parameter error + // even though it appears to be all correct, so reverting to untimed one until figure out why its erratic + vcos_semaphore_wait(&callback_data.complete_semaphore); + if (state.verbose) + fprintf(stderr, "Finished capture %d\n", frame); + } + + // Ensure we don't die if get callback with no open file + callback_data.file_handle = NULL; + + if (output_file != stdout) + { + rename_file(&state, output_file, final_filename, use_filename, frame); + } + else + { + fflush(output_file); + } + } + + if (use_filename) + { + free(use_filename); + use_filename = NULL; + } + if (final_filename) + { + free(final_filename); + final_filename = NULL; + } + } // end for (frame) + + vcos_semaphore_delete(&callback_data.complete_semaphore); + } + else + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + } + +error: + + mmal_status_to_int(status); + + if (state.verbose) + fprintf(stderr, "Closing down\n"); + + if (output_file) + fclose(output_file); + + // Disable all our ports that are not handled by connections + check_disable_port(camera_video_port); + + if (state.preview_connection) + mmal_connection_destroy(state.preview_connection); + + /* Disable components */ + if (state.preview_parameters.preview_component) + mmal_component_disable(state.preview_parameters.preview_component); + + if (state.camera_component) + mmal_component_disable(state.camera_component); + + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + + if (state.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + } + + if (status != MMAL_SUCCESS) + raspicamcontrol_check_configuration(128); + + return exit_code; +} + + + + diff --git a/host_applications/linux/apps/raspicam/RaspiTex.c b/host_applications/linux/apps/raspicam/RaspiTex.c new file mode 100755 index 0000000..f7cc08f --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTex.c @@ -0,0 +1,754 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "RaspiTex.h" +#include "RaspiCLI.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "RaspiTexUtil.h" +#include "interface/vcos/vcos.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "tga.h" + +#include "gl_scenes/mirror.h" +#include "gl_scenes/sobel.h" +#include "gl_scenes/square.h" +#include "gl_scenes/teapot.h" +#include "gl_scenes/vcsm_square.h" +#include "gl_scenes/yuv.h" + +/** + * \file RaspiTex.c + * + * A simple framework for extending a MMAL application to render buffers via + * OpenGL. + * + * MMAL buffers are often in YUV colour space and in either a planar or + * tile format which is not supported directly by V3D. Instead of copying + * the buffer from the GPU and doing a colour space / pixel format conversion + * the GL_OES_EGL_image_external is used. This allows an EGL image to be + * created from GPU buffer handle (MMAL opaque buffer handle). The EGL image + * may then be used to create a texture (glEGLImageTargetTexture2DOES) and + * drawn by either OpenGL ES 1.0 or 2.0 contexts. + * + * Notes: + * 1) GL_OES_EGL_image_external textures always return pixels in RGBA format. + * This is also the case when used from a fragment shader. + * + * 2) The driver implementation creates a new RGB_565 buffer and does the color + * space conversion from YUV. This happens in GPU memory using the vector + * processor. + * + * 3) Each EGL external image in use will consume GPU memory for the RGB 565 + * buffer. In addition, the GL pipeline might require more than one EGL image + * to be retained in GPU memory until the drawing commands are flushed. + * + * Typically 128 MB of GPU memory is sufficient for 720p viewfinder and 720p + * GL surface. If both the viewfinder and the GL surface are 1080p then + * 256MB of GPU memory is recommended, otherwise, for non-trivial scenes + * the system can run out of GPU memory whilst the camera is running. + * + * 4) It is important to make sure that the MMAL opaque buffer is not returned + * to MMAL before the GL driver has completed the asynchronous call to + * glEGLImageTargetTexture2DOES. Deferring destruction of the EGL image and + * the buffer return to MMAL until after eglSwapBuffers is the recommended. + * + * See also: http://www.khronos.org/registry/gles/extensions/OES/OES_EGL_image_external.txt + */ + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + +#define CommandGLScene 1 +#define CommandGLWin 2 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandGLScene, "-glscene", "gs", "GL scene square,teapot,mirror,yuv,sobel,vcsm_square", 1 }, + { CommandGLWin, "-glwin", "gw", "GL window settings <'x,y,w,h'>", 1 }, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + +/** + * Parse a possible command pair - command and parameter + * @param arg1 Command + * @param arg2 Parameter (could be NULL) + * @return How many parameters were used, 0,1,2 + */ +int raspitex_parse_cmdline(RASPITEX_STATE *state, + const char *arg1, const char *arg2) +{ + int command_id, used = 0, num_parameters; + + if (!arg1) + return 0; + + command_id = raspicli_get_command_id(cmdline_commands, + cmdline_commands_size, arg1, &num_parameters); + + // If invalid command, or we are missing a parameter, drop out + if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL)) + return 0; + + switch (command_id) + { + case CommandGLWin: // Allows a GL window to be different to preview-res + { + int tmp; + tmp = sscanf(arg2, "%d,%d,%d,%d", + &state->x, &state->y, &state->width, &state->height); + if (tmp != 4) + { + // Default to safe size on parse error + state->x = state->y = 0; + state->width = DEFAULT_WIDTH; + state->height = DEFAULT_HEIGHT; + } + else + { + state->gl_win_defined = 1; + } + + used = 2; + break; + } + + case CommandGLScene: // Selects the GL scene + { + if (strcmp(arg2, "square") == 0) + state->scene_id = RASPITEX_SCENE_SQUARE; + else if (strcmp(arg2, "teapot") == 0) + state->scene_id = RASPITEX_SCENE_TEAPOT; + else if (strcmp(arg2, "mirror") == 0) + state->scene_id = RASPITEX_SCENE_MIRROR; + else if (strcmp(arg2, "yuv") == 0) + state->scene_id = RASPITEX_SCENE_YUV; + else if (strcmp(arg2, "sobel") == 0) + state->scene_id = RASPITEX_SCENE_SOBEL; + else if (strcmp(arg2, "vcsm_square") == 0) + state->scene_id = RASPITEX_SCENE_VCSM_SQUARE; + else + vcos_log_error("Unknown scene %s", arg2); + + used = 2; + break; + } + } + return used; +} + +/** + * Display help for command line options + */ +void raspitex_display_help() +{ + fprintf(stdout, "\nPreview parameter commands\n\n"); + raspicli_display_help(cmdline_commands, cmdline_commands_size); +} + +static void update_fps() +{ + static int frame_count = 0; + static long long time_start = 0; + long long time_now; + struct timeval te; + float fps; + + frame_count++; + + gettimeofday(&te, NULL); + time_now = te.tv_sec * 1000LL + te.tv_usec / 1000; + + if (time_start == 0) + { + time_start = time_now; + } + else if (time_now - time_start > 5000) + { + fps = (float) frame_count / ((time_now - time_start) / 1000.0); + frame_count = 0; + time_start = time_now; + vcos_log_error("%3.2f FPS", fps); + } +} + +/** + * Captures the frame-buffer if requested. + * @param state RASPITEX STATE + * @return Zero if successful. + */ +static void raspitex_do_capture(RASPITEX_STATE *state) +{ + uint8_t *buffer = NULL; + size_t size = 0; + + if (state->capture.request) + { + if (state->ops.capture(state, &buffer, &size) == 0) + { + /* Pass ownership of buffer to main thread via capture state */ + state->capture.buffer = buffer; + state->capture.size = size; + } + else + { + state->capture.buffer = NULL; // Null indicates an error + state->capture.size = 0; + } + + state->capture.request = 0; // Always clear request and post sem + vcos_semaphore_post(&state->capture.completed_sem); + } +} + +/** + * Checks if there is at least one valid EGL image. + * @param state RASPITEX STATE + * @return Zero if successful. + */ +static int check_egl_image(RASPITEX_STATE *state) +{ + if (state->egl_image == EGL_NO_IMAGE_KHR && + state->y_egl_image == EGL_NO_IMAGE_KHR && + state->u_egl_image == EGL_NO_IMAGE_KHR && + state->v_egl_image == EGL_NO_IMAGE_KHR) + return -1; + else + return 0; +} + +/** + * Draws the next preview frame. If a new preview buffer is available then the + * preview texture is updated first. + * + * @param state RASPITEX STATE + * @param video_frame MMAL buffer header containing the opaque buffer handle. + * @return Zero if successful. + */ +static int raspitex_draw(RASPITEX_STATE *state, MMAL_BUFFER_HEADER_T *buf) +{ + int rc = 0; + + /* If buf is non-NULL then there is a new viewfinder frame available + * from the camera so the texture should be updated. + * + * Although it's possible to have multiple textures mapped to different + * viewfinder frames this can consume a lot of GPU memory for high-resolution + * viewfinders. + */ + if (buf) + { + /* Update the texture to the new viewfinder image which should */ + if (state->ops.update_texture) + { + rc = state->ops.update_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update RGBX texture %d", + VCOS_FUNCTION, rc); + goto end; + } + } + + if (state->ops.update_y_texture) + { + rc = state->ops.update_y_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update Y' plane texture %d", VCOS_FUNCTION, rc); + goto end; + } + } + + if (state->ops.update_u_texture) + { + rc = state->ops.update_u_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update U plane texture %d", VCOS_FUNCTION, rc); + goto end; + } + } + + if (state->ops.update_v_texture) + { + rc = state->ops.update_v_texture(state, (EGLClientBuffer) buf->data); + if (rc != 0) + { + vcos_log_error("%s: Failed to update V texture %d", VCOS_FUNCTION, rc); + goto end; + } + } + + /* Now return the PREVIOUS MMAL buffer header back to the camera preview. */ + if (state->preview_buf) + mmal_buffer_header_release(state->preview_buf); + + state->preview_buf = buf; + } + + /* Do the drawing */ + if (check_egl_image(state) == 0) + { + rc = state->ops.update_model(state); + if (rc != 0) + goto end; + + rc = state->ops.redraw(state); + if (rc != 0) + goto end; + + raspitex_do_capture(state); + + eglSwapBuffers(state->display, state->surface); + update_fps(); + } + else + { + // vcos_log_trace("%s: No preview image", VCOS_FUNCTION); + } + +end: + return rc; +} + +/** + * Process preview buffers. + * + * Dequeue each available preview buffer in order and call current redraw + * function. If no new buffers are available then the render function is + * invoked anyway. + * @param state The GL preview window state. + * @return Zero if successful. + */ +static int preview_process_returned_bufs(RASPITEX_STATE* state) +{ + MMAL_BUFFER_HEADER_T *buf; + int new_frame = 0; + int rc = 0; + + while ((buf = mmal_queue_get(state->preview_queue)) != NULL) + { + if (state->preview_stop == 0) + { + new_frame = 1; + rc = raspitex_draw(state, buf); + if (rc != 0) + { + vcos_log_error("%s: Error drawing frame. Stopping.", VCOS_FUNCTION); + state->preview_stop = 1; + return rc; + } + } + } + + /* If there were no new frames then redraw the scene again with the previous + * texture. Otherwise, go round the loop again to see if any new buffers + * are returned. + */ + if (! new_frame) + rc = raspitex_draw(state, NULL); + return rc; +} + +/** Preview worker thread. + * Ensures camera preview is supplied with buffers and sends preview frames to GL. + * @param arg Pointer to state. + * @return NULL always. + */ +static void *preview_worker(void *arg) +{ + RASPITEX_STATE* state = arg; + MMAL_PORT_T *preview_port = state->preview_port; + MMAL_BUFFER_HEADER_T *buf; + MMAL_STATUS_T st; + int rc; + + vcos_log_trace("%s: port %p", VCOS_FUNCTION, preview_port); + + rc = state->ops.create_native_window(state); + if (rc != 0) + goto end; + + rc = state->ops.gl_init(state); + if (rc != 0) + goto end; + + while (state->preview_stop == 0) + { + /* Send empty buffers to camera preview port */ + while ((buf = mmal_queue_get(state->preview_pool->queue)) != NULL) + { + st = mmal_port_send_buffer(preview_port, buf); + if (st != MMAL_SUCCESS) + { + vcos_log_error("Failed to send buffer to %s", preview_port->name); + } + } + /* Process returned buffers */ + if (preview_process_returned_bufs(state) != 0) + { + vcos_log_error("Preview error. Exiting."); + state->preview_stop = 1; + } + } + +end: + /* Make sure all buffers are returned on exit */ + while ((buf = mmal_queue_get(state->preview_queue)) != NULL) + mmal_buffer_header_release(buf); + + /* Tear down GL */ + state->ops.gl_term(state); + vcos_log_trace("Exiting preview worker"); + return NULL; +} + +/** + * MMAL Callback from camera preview output port. + * @param port The camera preview port. + * @param buf The new preview buffer. + **/ +static void preview_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) +{ + RASPITEX_STATE *state = (RASPITEX_STATE*) port->userdata; + + if (buf->length == 0) + { + vcos_log_trace("%s: zero-length buffer => EOS", port->name); + state->preview_stop = 1; + mmal_buffer_header_release(buf); + } + else if (buf->data == NULL) + { + vcos_log_trace("%s: zero buffer handle", port->name); + mmal_buffer_header_release(buf); + } + else + { + /* Enqueue the preview frame for rendering and return to + * avoid blocking MMAL core. + */ + mmal_queue_put(state->preview_queue, buf); + } +} + +/* Registers a callback on the camera preview port to receive + * notifications of new frames. + * This must be called before rapitex_start and may not be called again + * without calling raspitex_destroy first. + * + * @param state Pointer to the GL preview state. + * @param port Pointer to the camera preview port + * @return Zero if successful. + */ +int raspitex_configure_preview_port(RASPITEX_STATE *state, + MMAL_PORT_T *preview_port) +{ + MMAL_STATUS_T status; + vcos_log_trace("%s port %p", VCOS_FUNCTION, preview_port); + + /* Enable ZERO_COPY mode on the preview port which instructs MMAL to only + * pass the 4-byte opaque buffer handle instead of the contents of the opaque + * buffer. + * The opaque handle is resolved on VideoCore by the GL driver when the EGL + * image is created. + */ + status = mmal_port_parameter_set_boolean(preview_port, + MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to enable zero copy on camera preview port"); + goto end; + } + + status = mmal_port_format_commit(preview_port); + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto end; + } + + /* For GL a pool of opaque buffer handles must be allocated in the client. + * These buffers are used to create the EGL images. + */ + state->preview_port = preview_port; + preview_port->buffer_num = preview_port->buffer_num_recommended; + preview_port->buffer_size = preview_port->buffer_size_recommended; + + vcos_log_trace("Creating buffer pool for GL renderer num %d size %d", + preview_port->buffer_num, preview_port->buffer_size); + + /* Pool + queue to hold preview frames */ + state->preview_pool = mmal_port_pool_create(preview_port, + preview_port->buffer_num, preview_port->buffer_size); + + if (! state->preview_pool) + { + vcos_log_error("Error allocating pool"); + status = MMAL_ENOMEM; + goto end; + } + + /* Place filled buffers from the preview port in a queue to render */ + state->preview_queue = mmal_queue_create(); + if (! state->preview_queue) + { + vcos_log_error("Error allocating queue"); + status = MMAL_ENOMEM; + goto end; + } + + /* Enable preview port callback */ + preview_port->userdata = (struct MMAL_PORT_USERDATA_T*) state; + status = mmal_port_enable(preview_port, preview_output_cb); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to camera preview port"); + goto end; + } +end: + return (status == MMAL_SUCCESS ? 0 : -1); +} + +/* Initialises GL preview state and creates the dispmanx native window. + * @param state Pointer to the GL preview state. + * @return Zero if successful. + */ +int raspitex_init(RASPITEX_STATE *state) +{ + VCOS_STATUS_T status; + int rc; + vcos_init(); + + vcos_log_register("RaspiTex", VCOS_LOG_CATEGORY); + vcos_log_set_level(VCOS_LOG_CATEGORY, + state->verbose ? VCOS_LOG_INFO : VCOS_LOG_WARN); + vcos_log_trace("%s", VCOS_FUNCTION); + + status = vcos_semaphore_create(&state->capture.start_sem, + "glcap_start_sem", 1); + if (status != VCOS_SUCCESS) + goto error; + + status = vcos_semaphore_create(&state->capture.completed_sem, + "glcap_completed_sem", 0); + if (status != VCOS_SUCCESS) + goto error; + + switch (state->scene_id) + { + case RASPITEX_SCENE_SQUARE: + rc = square_open(state); + break; + case RASPITEX_SCENE_MIRROR: + rc = mirror_open(state); + break; + case RASPITEX_SCENE_TEAPOT: + rc = teapot_open(state); + break; + case RASPITEX_SCENE_YUV: + rc = yuv_open(state); + break; + case RASPITEX_SCENE_SOBEL: + rc = sobel_open(state); + break; + case RASPITEX_SCENE_VCSM_SQUARE: + rc = vcsm_square_open(state); + break; + default: + rc = -1; + break; + } + if (rc != 0) + goto error; + + return 0; + +error: + vcos_log_error("%s: failed", VCOS_FUNCTION); + return -1; +} + +/* Destroys the pools of buffers used by the GL renderer. + * @param state Pointer to the GL preview state. + */ +void raspitex_destroy(RASPITEX_STATE *state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + if (state->preview_pool) + { + mmal_pool_destroy(state->preview_pool); + state->preview_pool = NULL; + } + + if (state->preview_queue) + { + mmal_queue_destroy(state->preview_queue); + state->preview_queue = NULL; + } + + if (state->ops.destroy_native_window) + state->ops.destroy_native_window(state); + + if (state->ops.close) + state->ops.close(state); + + vcos_semaphore_delete(&state->capture.start_sem); + vcos_semaphore_delete(&state->capture.completed_sem); +} + +/* Initialise the GL / window state to sensible defaults. + * Also initialise any rendering parameters e.g. the scene + * + * @param state Pointer to the GL preview state. + * @return Zero if successful. + */ +void raspitex_set_defaults(RASPITEX_STATE *state) +{ + memset(state, 0, sizeof(*state)); + state->version_major = RASPITEX_VERSION_MAJOR; + state->version_minor = RASPITEX_VERSION_MINOR; + state->display = EGL_NO_DISPLAY; + state->surface = EGL_NO_SURFACE; + state->context = EGL_NO_CONTEXT; + state->egl_image = EGL_NO_IMAGE_KHR; + state->y_egl_image = EGL_NO_IMAGE_KHR; + state->u_egl_image = EGL_NO_IMAGE_KHR; + state->v_egl_image = EGL_NO_IMAGE_KHR; + state->opacity = 255; + state->width = DEFAULT_WIDTH; + state->height = DEFAULT_HEIGHT; + state->scene_id = RASPITEX_SCENE_SQUARE; + + state->ops.create_native_window = raspitexutil_create_native_window; + state->ops.gl_init = raspitexutil_gl_init_1_0; + state->ops.update_model = raspitexutil_update_model; + state->ops.redraw = raspitexutil_redraw; + state->ops.capture = raspitexutil_capture_bgra; + state->ops.gl_term = raspitexutil_gl_term; + state->ops.destroy_native_window = raspitexutil_destroy_native_window; + state->ops.close = raspitexutil_close; +} + +/* Stops the rendering loop and destroys MMAL resources + * @param state Pointer to the GL preview state. + */ +void raspitex_stop(RASPITEX_STATE *state) +{ + if (! state->preview_stop) + { + vcos_log_trace("Stopping GL preview"); + state->preview_stop = 1; + vcos_thread_join(&state->preview_thread, NULL); + } +} + +/** + * Starts the worker / GL renderer thread. + * @pre raspitex_init was successful + * @pre raspitex_configure_preview_port was successful + * @param state Pointer to the GL preview state. + * @return Zero on success, otherwise, -1 is returned + * */ +int raspitex_start(RASPITEX_STATE *state) +{ + VCOS_STATUS_T status; + + vcos_log_trace("%s", VCOS_FUNCTION); + status = vcos_thread_create(&state->preview_thread, "preview-worker", + NULL, preview_worker, state); + + if (status != VCOS_SUCCESS) + vcos_log_error("%s: Failed to start worker thread %d", + VCOS_FUNCTION, status); + + return (status == VCOS_SUCCESS ? 0 : -1); +} + +/** + * Writes the next GL frame-buffer to a RAW .ppm formatted file + * using the specified file-handle. + * @param state Pointer to the GL preview state. + * @param outpt_file Output file handle for the ppm image. + * @return Zero on success. + */ +int raspitex_capture(RASPITEX_STATE *state, FILE *output_file) +{ + int rc = 0; + uint8_t *buffer = NULL; + size_t size = 0; + + vcos_log_trace("%s: state %p file %p", VCOS_FUNCTION, + state, output_file); + + if (state && output_file) + { + /* Only request one capture at a time */ + vcos_semaphore_wait(&state->capture.start_sem); + state->capture.request = 1; + + /* Wait for capture to start */ + vcos_semaphore_wait(&state->capture.completed_sem); + + /* Take ownership of the captured buffer */ + buffer = state->capture.buffer; + size = state->capture.size; + + state->capture.request = 0; + state->capture.buffer = 0; + state->capture.size = 0; + + /* Allow another capture to be requested */ + vcos_semaphore_post(&state->capture.start_sem); + } + if (size == 0 || ! buffer) + { + vcos_log_error("%s: capture failed", VCOS_FUNCTION); + rc = -1; + goto end; + } + + raspitexutil_brga_to_rgba(buffer, size); + rc = write_tga(output_file, state->width, state->height, buffer, size); + fflush(output_file); + +end: + free(buffer); + return rc; +} diff --git a/host_applications/linux/apps/raspicam/RaspiTex.h b/host_applications/linux/apps/raspicam/RaspiTex.h new file mode 100755 index 0000000..712a5ba --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTex.h @@ -0,0 +1,192 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RASPITEX_H_ +#define RASPITEX_H_ + +#include +#include +#include +#include +#include +#include "interface/khronos/include/EGL/eglext_brcm.h" +#include "interface/mmal/mmal.h" + +#define RASPITEX_VERSION_MAJOR 1 +#define RASPITEX_VERSION_MINOR 0 + +typedef enum { + RASPITEX_SCENE_SQUARE = 0, + RASPITEX_SCENE_MIRROR, + RASPITEX_SCENE_TEAPOT, + RASPITEX_SCENE_YUV, + RASPITEX_SCENE_SOBEL, + RASPITEX_SCENE_VCSM_SQUARE, + +} RASPITEX_SCENE_T; + +struct RASPITEX_STATE; + +typedef struct RASPITEX_SCENE_OPS +{ + /// Creates a native window that will be used by egl_init + /// to create a window surface. + int (*create_native_window)(struct RASPITEX_STATE *state); + + /// Creates EGL surface for native window + int (*gl_init)(struct RASPITEX_STATE *state); + + /// Updates the RGBX texture from the next MMAL buffer + /// Set to null if this texture type is not required + int (*update_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + + /// Updates the Y' plane texture from the next MMAL buffer + /// Set to null if this texture type is not required + int (*update_y_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + + /// Updates the U plane texture from the next MMAL buffer + /// Set to null if this texture type is not required + int (*update_u_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + + /// Updates the V plane texture from the next MMAL buffer + /// Set to null if this texture type is not required + int (*update_v_texture)(struct RASPITEX_STATE *state, EGLClientBuffer mm_buf); + + /// Advance to the next animation step + int (*update_model)(struct RASPITEX_STATE *state); + + /// Draw the scene - called after update_model + int (*redraw)(struct RASPITEX_STATE *state); + + /// Allocates a buffer and copies the pixels from the current + /// frame-buffer into it. + int (*capture)(struct RASPITEX_STATE *state, + uint8_t **buffer, size_t *buffer_size); + + /// Creates EGL surface for native window + void (*gl_term)(struct RASPITEX_STATE *state); + + /// Destroys the native window + void (*destroy_native_window)(struct RASPITEX_STATE *state); + + /// Called when the scene is unloaded + void (*close)(struct RASPITEX_STATE *state); +} RASPITEX_SCENE_OPS; + +typedef struct RASPITEX_CAPTURE +{ + /// Wait for previous capture to complete + VCOS_SEMAPHORE_T start_sem; + + /// Posted once the capture is complete + VCOS_SEMAPHORE_T completed_sem; + + /// The RGB capture buffer + uint8_t *buffer; + + /// Size of the captured buffer in bytes + size_t size; + + /// Frame-buffer capture has been requested. Could use + /// a queue instead here to allow multiple capture requests. + int request; +} RASPITEX_CAPTURE; + +/** + * Contains the internal state and configuration for the GL rendered + * preview window. + */ +typedef struct RASPITEX_STATE +{ + int version_major; /// For binary compatibility + int version_minor; /// Incremented for new features + MMAL_PORT_T *preview_port; /// Source port for preview opaque buffers + MMAL_POOL_T *preview_pool; /// Pool for storing opaque buffer handles + MMAL_QUEUE_T *preview_queue; /// Queue preview buffers to display in order + VCOS_THREAD_T preview_thread; /// Preview worker / GL rendering thread + uint32_t preview_stop; /// If zero the worker can continue + + /* Copy of preview window params */ + int32_t preview_x; /// x-offset of preview window + int32_t preview_y; /// y-offset of preview window + int32_t preview_width; /// preview y-plane width in pixels + int32_t preview_height; /// preview y-plane height in pixels + + /* Display rectangle for the native window */ + int32_t x; /// x-offset in pixels + int32_t y; /// y-offset in pixels + int32_t width; /// width in pixels + int32_t height; /// height in pixels + int opacity; /// Alpha value for display element + int gl_win_defined; /// Use rect from --glwin instead of preview + + /* DispmanX info. This might be unused if a custom create_native_window + * does something else. */ + DISPMANX_DISPLAY_HANDLE_T disp; /// Dispmanx display for GL preview + EGL_DISPMANX_WINDOW_T win; /// Dispmanx handle for preview surface + + EGLNativeWindowType* native_window; /// Native window used for EGL surface + EGLDisplay display; /// The current EGL display + EGLSurface surface; /// The current EGL surface + EGLContext context; /// The current EGL context + const EGLint *egl_config_attribs; /// GL scenes preferred EGL configuration + + GLuint texture; /// Name for the preview texture + EGLImageKHR egl_image; /// The current preview EGL image + + GLuint y_texture; /// The Y plane texture + EGLImageKHR y_egl_image; /// EGL image for Y plane texture + GLuint u_texture; /// The U plane texture + EGLImageKHR u_egl_image; /// EGL image for U plane texture + GLuint v_texture; /// The V plane texture + EGLImageKHR v_egl_image; /// EGL image for V plane texture + + MMAL_BUFFER_HEADER_T *preview_buf; /// MMAL buffer currently bound to texture(s) + + RASPITEX_SCENE_T scene_id; /// Id of the scene to load + RASPITEX_SCENE_OPS ops; /// The interface for the current scene + void *scene_state; /// Pointer to scene specific data + int verbose; /// Log FPS + + RASPITEX_CAPTURE capture; /// Frame-buffer capture state + +} RASPITEX_STATE; + +int raspitex_init(RASPITEX_STATE *state); +void raspitex_destroy(RASPITEX_STATE *state); +int raspitex_start(RASPITEX_STATE *state); +void raspitex_stop(RASPITEX_STATE *state); +void raspitex_set_defaults(RASPITEX_STATE *state); +int raspitex_configure_preview_port(RASPITEX_STATE *state, + MMAL_PORT_T *preview_port); +void raspitex_display_help(); +int raspitex_parse_cmdline(RASPITEX_STATE *state, + const char *arg1, const char *arg2); +int raspitex_capture(RASPITEX_STATE *state, FILE* output_file); + +#endif /* RASPITEX_H_ */ diff --git a/host_applications/linux/apps/raspicam/RaspiTexUtil.c b/host_applications/linux/apps/raspicam/RaspiTexUtil.c new file mode 100755 index 0000000..d5b5954 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTexUtil.c @@ -0,0 +1,597 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "RaspiTexUtil.h" +#include "RaspiTex.h" +#include +#include + +VCOS_LOG_CAT_T raspitex_log_category; + +/** + * \file RaspiTexUtil.c + * + * Provides default implementations for the raspitex_scene_ops functions + * and general utility functions. + */ + +/** + * Deletes textures and EGL surfaces and context. + * @param raspitex_state Pointer to the Raspi + */ +void raspitexutil_gl_term(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + + /* Delete OES textures */ + glDeleteTextures(1, &raspitex_state->texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->egl_image); + raspitex_state->egl_image = EGL_NO_IMAGE_KHR; + + glDeleteTextures(1, &raspitex_state->y_texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->y_egl_image); + raspitex_state->y_egl_image = EGL_NO_IMAGE_KHR; + + glDeleteTextures(1, &raspitex_state->u_texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->u_egl_image); + raspitex_state->u_egl_image = EGL_NO_IMAGE_KHR; + + glDeleteTextures(1, &raspitex_state->v_texture); + eglDestroyImageKHR(raspitex_state->display, raspitex_state->v_egl_image); + raspitex_state->v_egl_image = EGL_NO_IMAGE_KHR; + + /* Terminate EGL */ + eglMakeCurrent(raspitex_state->display, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(raspitex_state->display, raspitex_state->context); + eglDestroySurface(raspitex_state->display, raspitex_state->surface); + eglTerminate(raspitex_state->display); +} + +/** Creates a native window for the GL surface using dispmanx + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful, otherwise, -1 is returned. + */ +int raspitexutil_create_native_window(RASPITEX_STATE *raspitex_state) +{ + VC_DISPMANX_ALPHA_T alpha = {DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0}; + VC_RECT_T src_rect = {0}; + VC_RECT_T dest_rect = {0}; + uint32_t disp_num = 0; // Primary + uint32_t layer_num = 0; + DISPMANX_ELEMENT_HANDLE_T elem; + DISPMANX_UPDATE_HANDLE_T update; + + alpha.opacity = raspitex_state->opacity; + dest_rect.x = raspitex_state->x; + dest_rect.y = raspitex_state->y; + dest_rect.width = raspitex_state->width; + dest_rect.height = raspitex_state->height; + + vcos_log_trace("%s: %d,%d,%d,%d %d,%d,0x%x,0x%x", VCOS_FUNCTION, + src_rect.x, src_rect.y, src_rect.width, src_rect.height, + dest_rect.x, dest_rect.y, dest_rect.width, dest_rect.height); + + src_rect.width = dest_rect.width << 16; + src_rect.height = dest_rect.height << 16; + + raspitex_state->disp = vc_dispmanx_display_open(disp_num); + if (raspitex_state->disp == DISPMANX_NO_HANDLE) + { + vcos_log_error("Failed to open display handle"); + goto error; + } + + update = vc_dispmanx_update_start(0); + if (update == DISPMANX_NO_HANDLE) + { + vcos_log_error("Failed to open update handle"); + goto error; + } + + elem = vc_dispmanx_element_add(update, raspitex_state->disp, layer_num, + &dest_rect, 0, &src_rect, DISPMANX_PROTECTION_NONE, &alpha, NULL, + DISPMANX_NO_ROTATE); + if (elem == DISPMANX_NO_HANDLE) + { + vcos_log_error("Failed to create element handle"); + goto error; + } + + raspitex_state->win.element = elem; + raspitex_state->win.width = raspitex_state->width; + raspitex_state->win.height = raspitex_state->height; + vc_dispmanx_update_submit_sync(update); + + raspitex_state->native_window = (EGLNativeWindowType*) &raspitex_state->win; + + return 0; +error: + return -1; +} + +/** Destroys the pools of buffers used by the GL renderer. + * @param raspitex_state A pointer to the GL preview state. + */ +void raspitexutil_destroy_native_window(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + if (raspitex_state->disp != DISPMANX_NO_HANDLE) + { + vc_dispmanx_display_close(raspitex_state->disp); + raspitex_state->disp = DISPMANX_NO_HANDLE; + } +} + +/** Creates the EGL context and window surface for the native window + * using specified arguments. + * @param raspitex_state A pointer to the GL preview state. This contains + * the native_window pointer. + * @param attribs The config attributes. + * @param context_attribs The context attributes. + * @return Zero if successful. + */ +static int raspitexutil_gl_common(RASPITEX_STATE *raspitex_state, + const EGLint attribs[], const EGLint context_attribs[]) +{ + EGLConfig config; + EGLint num_configs; + + vcos_log_trace("%s", VCOS_FUNCTION); + + if (raspitex_state->native_window == NULL) + { + vcos_log_error("%s: No native window", VCOS_FUNCTION); + goto error; + } + + raspitex_state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (raspitex_state->display == EGL_NO_DISPLAY) + { + vcos_log_error("%s: Failed to get EGL display", VCOS_FUNCTION); + goto error; + } + + if (! eglInitialize(raspitex_state->display, 0, 0)) + { + vcos_log_error("%s: eglInitialize failed", VCOS_FUNCTION); + goto error; + } + + if (! eglChooseConfig(raspitex_state->display, attribs, &config, + 1, &num_configs)) + { + vcos_log_error("%s: eglChooseConfig failed", VCOS_FUNCTION); + goto error; + } + + raspitex_state->surface = eglCreateWindowSurface(raspitex_state->display, + config, raspitex_state->native_window, NULL); + if (raspitex_state->surface == EGL_NO_SURFACE) + { + vcos_log_error("%s: eglCreateWindowSurface failed", VCOS_FUNCTION); + goto error; + } + + raspitex_state->context = eglCreateContext(raspitex_state->display, + config, EGL_NO_CONTEXT, context_attribs); + if (raspitex_state->context == EGL_NO_CONTEXT) + { + vcos_log_error("%s: eglCreateContext failed", VCOS_FUNCTION); + goto error; + } + + if (!eglMakeCurrent(raspitex_state->display, raspitex_state->surface, + raspitex_state->surface, raspitex_state->context)) + { + vcos_log_error("%s: Failed to activate EGL context", VCOS_FUNCTION); + goto error; + } + + return 0; + +error: + vcos_log_error("%s: EGL error 0x%08x", VCOS_FUNCTION, eglGetError()); + raspitex_state->ops.gl_term(raspitex_state); + return -1; +} + +/* Creates the RGBA and luma textures with some default parameters + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +int raspitexutil_create_textures(RASPITEX_STATE *raspitex_state) +{ + GLCHK(glGenTextures(1, &raspitex_state->texture)); + GLCHK(glGenTextures(1, &raspitex_state->y_texture)); + GLCHK(glGenTextures(1, &raspitex_state->u_texture)); + GLCHK(glGenTextures(1, &raspitex_state->v_texture)); + return 0; +} + +/** + * Creates an OpenGL ES 1.X context. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +int raspitexutil_gl_init_1_0(RASPITEX_STATE *raspitex_state) +{ + int rc; + const EGLint* attribs = raspitex_state->egl_config_attribs; + + const EGLint default_attribs[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, + EGL_NONE + }; + + const EGLint context_attribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 1, + EGL_NONE + }; + + if (! attribs) + attribs = default_attribs; + + rc = raspitexutil_gl_common(raspitex_state, attribs, context_attribs); + if (rc != 0) + goto end; + + GLCHK(glEnable(GL_TEXTURE_EXTERNAL_OES)); + rc = raspitexutil_create_textures(raspitex_state); + +end: + return rc; +} + +/** + * Creates an OpenGL ES 2.X context. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +int raspitexutil_gl_init_2_0(RASPITEX_STATE *raspitex_state) +{ + int rc; + const EGLint* attribs = raspitex_state->egl_config_attribs;; + + const EGLint default_attribs[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + const EGLint context_attribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + if (! attribs) + attribs = default_attribs; + + vcos_log_trace("%s", VCOS_FUNCTION); + rc = raspitexutil_gl_common(raspitex_state, attribs, context_attribs); + if (rc != 0) + goto end; + + rc = raspitexutil_create_textures(raspitex_state); +end: + return rc; +} + +/** + * Advances the texture and EGL image to the next MMAL buffer. + * + * @param display The EGL display. + * @param target The EGL image target e.g. EGL_IMAGE_BRCM_MULTIMEDIA + * @param mm_buf The EGL client buffer (mmal opaque buffer) that is used to + * create the EGL Image for the preview texture. + * @param egl_image Pointer to the EGL image to update with mm_buf. + * @param texture Pointer to the texture to update from EGL image. + * @return Zero if successful. + */ +int raspitexutil_do_update_texture(EGLDisplay display, EGLenum target, + EGLClientBuffer mm_buf, GLuint *texture, EGLImageKHR *egl_image) +{ + vcos_log_trace("%s: mm_buf %u", VCOS_FUNCTION, (unsigned) mm_buf); + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, *texture)); + if (*egl_image != EGL_NO_IMAGE_KHR) + { + /* Discard the EGL image for the preview frame */ + eglDestroyImageKHR(display, *egl_image); + *egl_image = EGL_NO_IMAGE_KHR; + } + + *egl_image = eglCreateImageKHR(display, EGL_NO_CONTEXT, target, mm_buf, NULL); + GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, *egl_image)); + + return 0; +} + +/** + * Updates the RGBX texture to the specified MMAL buffer. + * @param raspitex_state A pointer to the GL preview state. + * @param mm_buf The MMAL buffer. + * @return Zero if successful. + */ +int raspitexutil_update_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf) +{ + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA, mm_buf, + &raspitex_state->texture, &raspitex_state->egl_image); +} + +/** + * Updates the Y plane texture to the specified MMAL buffer. + * @param raspitex_state A pointer to the GL preview state. + * @param mm_buf The MMAL buffer. + * @return Zero if successful. + */ +int raspitexutil_update_y_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf) +{ + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA_Y, mm_buf, + &raspitex_state->y_texture, &raspitex_state->y_egl_image); +} + +/** + * Updates the U plane texture to the specified MMAL buffer. + * @param raspitex_state A pointer to the GL preview state. + * @param mm_buf The MMAL buffer. + * @return Zero if successful. + */ +int raspitexutil_update_u_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf) +{ + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA_U, mm_buf, + &raspitex_state->u_texture, &raspitex_state->u_egl_image); +} + +/** + * Updates the V plane texture to the specified MMAL buffer. + * @param raspitex_state A pointer to the GL preview state. + * @param mm_buf The MMAL buffer. + * @return Zero if successful. + */ +int raspitexutil_update_v_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf) +{ + return raspitexutil_do_update_texture(raspitex_state->display, + EGL_IMAGE_BRCM_MULTIMEDIA_V, mm_buf, + &raspitex_state->v_texture, &raspitex_state->v_egl_image); +} + +/** + * Default is a no-op + * @param raspitex_state A pointer to the GL preview state. + * @return Zero. + */ +int raspitexutil_update_model(RASPITEX_STATE* raspitex_state) +{ + (void) raspitex_state; + return 0; +} + +/** + * Default is a no-op + * @param raspitex_state A pointer to the GL preview state. + * @return Zero. + */ +int raspitexutil_redraw(RASPITEX_STATE* raspitex_state) +{ + (void) raspitex_state; + return 0; +} + +/** + * Default is a no-op + * @param raspitex_state A pointer to the GL preview state. + */ +void raspitexutil_close(RASPITEX_STATE* raspitex_state) +{ + (void) raspitex_state; +} + +/** + * Performs an in-place byte swap from BGRA to RGBA. + * @param buffer The buffer to modify. + * @param size Size of the buffer in bytes. + */ +void raspitexutil_brga_to_rgba(uint8_t *buffer, size_t size) +{ + uint8_t* out = buffer; + uint8_t* end = buffer + size; + + while (out < end) + { + uint8_t tmp = out[0]; + out[0] = out[2]; + out[2] = tmp; + out += 4; + } +} + +/** + * Uses glReadPixels to grab the current frame-buffer contents + * and returns the result in a newly allocate buffer along with + * the its size. + * Data is returned in BGRA format for TGA output. PPM output doesn't + * require the channel order swap but would require a vflip. The TGA + * format also supports alpha. The byte swap is not done in this function + * to avoid blocking the GL rendering thread. + * @param state Pointer to the GL preview state. + * @param buffer Address of pointer to set to pointer to new buffer. + * @param buffer_size The size of the new buffer in bytes (out param) + * @return Zero if successful. + */ +int raspitexutil_capture_bgra(RASPITEX_STATE *state, + uint8_t **buffer, size_t *buffer_size) +{ + const int bytes_per_pixel = 4; + + vcos_log_trace("%s: %dx%d %d", VCOS_FUNCTION, + state->width, state->height, bytes_per_pixel); + + *buffer_size = state->width * state->height * bytes_per_pixel; + *buffer = calloc(*buffer_size, 1); + if (! *buffer) + goto error; + + glReadPixels(0, 0, state->width, state->height, GL_RGBA, + GL_UNSIGNED_BYTE, *buffer); + if (glGetError() != GL_NO_ERROR) + goto error; + + return 0; + +error: + *buffer_size = 0; + if (*buffer) + free(*buffer); + *buffer = NULL; + return -1; +} + + +/** + * Takes a description of shader program, compiles it and gets the locations + * of uniforms and attributes. + * + * @param p The shader program state. + * @return Zero if successful. + */ +int raspitexutil_build_shader_program(RASPITEXUTIL_SHADER_PROGRAM_T *p) +{ + GLint status; + int i = 0; + char log[1024]; + int logLen = 0; + vcos_assert(p); + vcos_assert(p->vertex_source); + vcos_assert(p->fragment_source); + + if (! (p && p->vertex_source && p->fragment_source)) + goto fail; + + p->vs = p->fs = 0; + + p->vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(p->vs, 1, &p->vertex_source, NULL); + glCompileShader(p->vs); + glGetShaderiv(p->vs, GL_COMPILE_STATUS, &status); + if (! status) { + glGetShaderInfoLog(p->vs, sizeof(log), &logLen, log); + vcos_log_error("Program info log %s", log); + goto fail; + } + + p->fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(p->fs, 1, &p->fragment_source, NULL); + glCompileShader(p->fs); + + glGetShaderiv(p->fs, GL_COMPILE_STATUS, &status); + if (! status) { + glGetShaderInfoLog(p->fs, sizeof(log), &logLen, log); + vcos_log_error("Program info log %s", log); + goto fail; + } + + p->program = glCreateProgram(); + glAttachShader(p->program, p->vs); + glAttachShader(p->program, p->fs); + glLinkProgram(p->program); + glGetProgramiv(p->program, GL_LINK_STATUS, &status); + if (! status) + { + vcos_log_error("Failed to link shader program"); + glGetProgramInfoLog(p->program, sizeof(log), &logLen, log); + vcos_log_error("Program info log %s", log); + goto fail; + } + + for (i = 0; i < SHADER_MAX_ATTRIBUTES; ++i) + { + if (! p->attribute_names[i]) + break; + p->attribute_locations[i] = glGetAttribLocation(p->program, p->attribute_names[i]); + if (p->attribute_locations[i] == -1) + { + vcos_log_error("Failed to get location for attribute %s", + p->attribute_names[i]); + goto fail; + } + else { + vcos_log_trace("Attribute for %s is %d", + p->attribute_names[i], p->attribute_locations[i]); + } + } + + for (i = 0; i < SHADER_MAX_UNIFORMS; ++i) + { + if (! p->uniform_names[i]) + break; + p->uniform_locations[i] = glGetUniformLocation(p->program, p->uniform_names[i]); + if (p->uniform_locations[i] == -1) + { + vcos_log_error("Failed to get location for uniform %s", + p->uniform_names[i]); + goto fail; + } + else { + vcos_log_trace("Uniform for %s is %d", + p->uniform_names[i], p->uniform_locations[i]); + } + } + + return 0; + +fail: + vcos_log_error("%s: Failed to build shader program", VCOS_FUNCTION); + if (p) + { + glDeleteProgram(p->program); + glDeleteShader(p->fs); + glDeleteShader(p->vs); + } + return -1; +} + diff --git a/host_applications/linux/apps/raspicam/RaspiTexUtil.h b/host_applications/linux/apps/raspicam/RaspiTexUtil.h new file mode 100755 index 0000000..cf5dfb3 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiTexUtil.h @@ -0,0 +1,116 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RASPITEX_UTIL_H_ +#define RASPITEX_UTIL_H_ + +#define VCOS_LOG_CATEGORY (&raspitex_log_category) +#include +#include +#include +#include +#include +#include +#include "RaspiTex.h" +#include "interface/vcos/vcos.h" + +extern VCOS_LOG_CAT_T raspitex_log_category; + +#define SHADER_MAX_ATTRIBUTES 16 +#define SHADER_MAX_UNIFORMS 16 +/** + * Container for a simple shader program. The uniform and attribute locations + * are automatically setup by raspitex_build_shader_program. + */ +typedef struct RASPITEXUTIL_SHADER_PROGRAM_T +{ + const char *vertex_source; /// Pointer to vertex shader source + const char *fragment_source; /// Pointer to fragment shader source + + /// Array of uniform names for raspitex_build_shader_program to process + const char *uniform_names[SHADER_MAX_UNIFORMS]; + /// Array of attribute names for raspitex_build_shader_program to process + const char *attribute_names[SHADER_MAX_ATTRIBUTES]; + + GLint vs; /// Vertex shader handle + GLint fs; /// Fragment shader handle + GLint program; /// Shader program handle + + /// The locations for uniforms defined in uniform_names + GLint uniform_locations[SHADER_MAX_UNIFORMS]; + + /// The locations for attributes defined in attribute_names + GLint attribute_locations[SHADER_MAX_ATTRIBUTES]; +} RASPITEXUTIL_SHADER_PROGRAM_T; + + +/* Uncomment to enable extra GL error checking */ +//#define CHECK_GL_ERRORS +#if defined(CHECK_GL_ERRORS) +#define GLCHK(X) \ +do { \ + GLenum err = GL_NO_ERROR; \ + X; \ + while ((err = glGetError())) \ + { \ + vcos_log_error("GL error 0x%x in " #X "file %s line %d", err, __FILE__,__LINE__); \ + vcos_assert(err == GL_NO_ERROR); \ + exit(err); \ + } \ +} \ +while(0) +#else +#define GLCHK(X) X +#endif /* CHECK_GL_ERRORS */ + +/* Default GL scene ops functions */ +int raspitexutil_create_native_window(RASPITEX_STATE *raspitex_state); +int raspitexutil_gl_init_1_0(RASPITEX_STATE *raspitex_state); +int raspitexutil_gl_init_2_0(RASPITEX_STATE *raspitex_state); +int raspitexutil_update_model(RASPITEX_STATE* raspitex_state); +int raspitexutil_redraw(RASPITEX_STATE* raspitex_state); +void raspitexutil_gl_term(RASPITEX_STATE *raspitex_state); +void raspitexutil_destroy_native_window(RASPITEX_STATE *raspitex_state); +int raspitexutil_create_textures(RASPITEX_STATE *raspitex_state); +int raspitexutil_update_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +int raspitexutil_update_y_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +int raspitexutil_update_u_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +int raspitexutil_update_v_texture(RASPITEX_STATE *raspitex_state, + EGLClientBuffer mm_buf); +int raspitexutil_capture_bgra(struct RASPITEX_STATE *state, + uint8_t **buffer, size_t *buffer_size); +void raspitexutil_close(RASPITEX_STATE* raspitex_state); + +/* Utility functions */ +int raspitexutil_build_shader_program(RASPITEXUTIL_SHADER_PROGRAM_T *p); +void raspitexutil_brga_to_rgba(uint8_t *buffer, size_t size); + +#endif /* RASPITEX_UTIL_H_ */ diff --git a/host_applications/linux/apps/raspicam/RaspiVid.c b/host_applications/linux/apps/raspicam/RaspiVid.c new file mode 100755 index 0000000..20b0ad5 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiVid.c @@ -0,0 +1,3018 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, James Hughes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiVid.c + * Command line program to capture a camera video stream and encode it to file. + * Also optionally display a preview/viewfinder of current camera input. + * + * \date 28th Feb 2013 + * \Author: James Hughes + * + * Description + * + * 3 components are created; camera, preview and video encoder. + * Camera component has three ports, preview, video and stills. + * This program connects preview and video to the preview and video + * encoder. Using mmal we don't need to worry about buffers between these + * components, but we do need to handle buffers from the encoder, which + * are simply written straight to the file in the requisite buffer callback. + * + * If raw option is selected, a video splitter component is connected between + * camera and preview. This allows us to set up callback for raw camera data + * (in YUV420 or RGB format) which might be useful for further image processing. + * + * We use the RaspiCamControl code to handle the specific camera settings. + * We use the RaspiPreview code to handle the (generic) preview window + */ + +// We use some GNU extensions (basename) +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define VERSION_STRING "v1.3.12" + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" +#include "interface/mmal/mmal_parameters_camera.h" + +#include "RaspiCamControl.h" +#include "RaspiPreview.h" +#include "RaspiCLI.h" + +#include + +#include + +// Standard port setting for the camera component +#define MMAL_CAMERA_PREVIEW_PORT 0 +#define MMAL_CAMERA_VIDEO_PORT 1 +#define MMAL_CAMERA_CAPTURE_PORT 2 + +// Port configuration for the splitter component +#define SPLITTER_OUTPUT_PORT 0 +#define SPLITTER_PREVIEW_PORT 1 + +// Video format information +// 0 implies variable +#define VIDEO_FRAME_RATE_NUM 30 +#define VIDEO_FRAME_RATE_DEN 1 + +/// Video render needs at least 2 buffers. +#define VIDEO_OUTPUT_BUFFERS_NUM 3 + +// Max bitrate we allow for recording +const int MAX_BITRATE_MJPEG = 25000000; // 25Mbits/s +const int MAX_BITRATE_LEVEL4 = 25000000; // 25Mbits/s +const int MAX_BITRATE_LEVEL42 = 62500000; // 62.5Mbits/s + +/// Interval at which we check for an failure abort during capture +const int ABORT_INTERVAL = 100; // ms + + +/// Capture/Pause switch method +/// Simply capture for time specified +#define WAIT_METHOD_NONE 0 +/// Cycle between capture and pause for times specified +#define WAIT_METHOD_TIMED 1 +/// Switch between capture and pause on keypress +#define WAIT_METHOD_KEYPRESS 2 +/// Switch between capture and pause on signal +#define WAIT_METHOD_SIGNAL 3 +/// Run/record forever +#define WAIT_METHOD_FOREVER 4 + + + +int mmal_status_to_int(MMAL_STATUS_T status); +static void signal_handler(int signal_number); + +// Forward +typedef struct RASPIVID_STATE_S RASPIVID_STATE; + +/** Struct used to pass information in encoder port userdata to callback + */ +typedef struct +{ + FILE *file_handle; /// File handle to write buffer data to. + RASPIVID_STATE *pstate; /// pointer to our state in case required in callback + int abort; /// Set to 1 in callback if an error occurs to attempt to abort the capture + char *cb_buff; /// Circular buffer + int cb_len; /// Length of buffer + int cb_wptr; /// Current write pointer + int cb_wrap; /// Has buffer wrapped at least once? + int cb_data; /// Valid bytes in buffer +#define IFRAME_BUFSIZE (60*1000) + int iframe_buff[IFRAME_BUFSIZE]; /// buffer of iframe pointers + int iframe_buff_wpos; + int iframe_buff_rpos; + char header_bytes[29]; + int header_wptr; + FILE *imv_file_handle; /// File handle to write inline motion vectors to. + FILE *raw_file_handle; /// File handle to write raw data to. + int flush_buffers; + FILE *pts_file_handle; /// File timestamps +} PORT_USERDATA; + +/** Possible raw output formats + */ +typedef enum { + RAW_OUTPUT_FMT_YUV = 1, + RAW_OUTPUT_FMT_RGB, + RAW_OUTPUT_FMT_GRAY, +} RAW_OUTPUT_FMT; + +/** Structure containing all state information for the current run + */ +struct RASPIVID_STATE_S +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + MMAL_FOURCC_T encoding; /// Requested codec video encoding (MJPEG or H264) + int bitrate; /// Requested bitrate + int framerate; /// Requested frame rate (fps) + int intraperiod; /// Intra-refresh period (key frame rate) + int quantisationParameter; /// Quantisation parameter - quality. Set bitrate 0 and set this for variable bitrate + int bInlineHeaders; /// Insert inline headers to stream (SPS, PPS) + char *filename; /// filename of output file + int verbose; /// !0 if want detailed run information + int demoMode; /// Run app in demo mode + int demoInterval; /// Interval between camera settings changes + int immutableInput; /// Flag to specify whether encoder works in place or creates a new buffer. Result is preview can display either + /// the camera output or the encoder output (with compression artifacts) + int profile; /// H264 profile to use for encoding + int level; /// H264 level to use for encoding + int waitMethod; /// Method for switching between pause and capture + + int onTime; /// In timed cycle mode, the amount of time the capture is on per cycle + int offTime; /// In timed cycle mode, the amount of time the capture is off per cycle + + int segmentSize; /// Segment mode In timed cycle mode, the amount of time the capture is off per cycle + int segmentWrap; /// Point at which to wrap segment counter + int segmentNumber; /// Current segment counter + int splitNow; /// Split at next possible i-frame if set to 1. + int splitWait; /// Switch if user wants splited files + + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component + MMAL_COMPONENT_T *splitter_component; /// Pointer to the splitter component + MMAL_COMPONENT_T *encoder_component; /// Pointer to the encoder component + MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera or splitter to preview + MMAL_CONNECTION_T *splitter_connection;/// Pointer to the connection from camera to splitter + MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder + + MMAL_POOL_T *splitter_pool; /// Pointer to the pool of buffers used by splitter output port 0 + MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port + + PORT_USERDATA callback_data; /// Used to move data to the encoder callback + + int bCapturing; /// State of capture/pause + int bCircularBuffer; /// Whether we are writing to a circular buffer + + int inlineMotionVectors; /// Encoder outputs inline Motion Vectors + char *imv_filename; /// filename of inline Motion Vectors output + int raw_output; /// Output raw video from camera as well + RAW_OUTPUT_FMT raw_output_fmt; /// The raw video format + char *raw_filename; /// Filename for raw video output + int cameraNum; /// Camera number + int settings; /// Request settings from the camera + int sensor_mode; /// Sensor mode. 0=auto. Check docs/forum for modes selected by other values. + int intra_refresh_type; /// What intra refresh type to use. -1 to not set. + int frame; + char *pts_filename; + int save_pts; + int64_t starttime; + int64_t lasttime; + + bool netListen; +}; + + +/// Structure to cross reference H264 profile strings against the MMAL parameter equivalent +static XREF_T profile_map[] = +{ + {"baseline", MMAL_VIDEO_PROFILE_H264_BASELINE}, + {"main", MMAL_VIDEO_PROFILE_H264_MAIN}, + {"high", MMAL_VIDEO_PROFILE_H264_HIGH}, +// {"constrained", MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE} // Does anyone need this? +}; + +static int profile_map_size = sizeof(profile_map) / sizeof(profile_map[0]); + +/// Structure to cross reference H264 level strings against the MMAL parameter equivalent +static XREF_T level_map[] = +{ + {"4", MMAL_VIDEO_LEVEL_H264_4}, + {"4.1", MMAL_VIDEO_LEVEL_H264_41}, + {"4.2", MMAL_VIDEO_LEVEL_H264_42}, +}; + +static int level_map_size = sizeof(level_map) / sizeof(level_map[0]); + +static XREF_T initial_map[] = +{ + {"record", 0}, + {"pause", 1}, +}; + +static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]); + +static XREF_T intra_refresh_map[] = +{ + {"cyclic", MMAL_VIDEO_INTRA_REFRESH_CYCLIC}, + {"adaptive", MMAL_VIDEO_INTRA_REFRESH_ADAPTIVE}, + {"both", MMAL_VIDEO_INTRA_REFRESH_BOTH}, + {"cyclicrows", MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS}, +// {"random", MMAL_VIDEO_INTRA_REFRESH_PSEUDO_RAND} Cannot use random, crashes the encoder. No idea why. +}; + +static int intra_refresh_map_size = sizeof(intra_refresh_map) / sizeof(intra_refresh_map[0]); + +static XREF_T raw_output_fmt_map[] = +{ + {"yuv", RAW_OUTPUT_FMT_YUV}, + {"rgb", RAW_OUTPUT_FMT_RGB}, + {"gray", RAW_OUTPUT_FMT_GRAY}, +}; + +static int raw_output_fmt_map_size = sizeof(raw_output_fmt_map) / sizeof(raw_output_fmt_map[0]); + +static void display_valid_parameters(char *app_name); + +/// Command ID's and Structure defining our command line options +#define CommandHelp 0 +#define CommandWidth 1 +#define CommandHeight 2 +#define CommandBitrate 3 +#define CommandOutput 4 +#define CommandVerbose 5 +#define CommandTimeout 6 +#define CommandDemoMode 7 +#define CommandFramerate 8 +#define CommandPreviewEnc 9 +#define CommandIntraPeriod 10 +#define CommandProfile 11 +#define CommandTimed 12 +#define CommandSignal 13 +#define CommandKeypress 14 +#define CommandInitialState 15 +#define CommandQP 16 +#define CommandInlineHeaders 17 +#define CommandSegmentFile 18 +#define CommandSegmentWrap 19 +#define CommandSegmentStart 20 +#define CommandSplitWait 21 +#define CommandCircular 22 +#define CommandIMV 23 +#define CommandCamSelect 24 +#define CommandSettings 25 +#define CommandSensorMode 26 +#define CommandIntraRefreshType 27 +#define CommandFlush 28 +#define CommandSavePTS 29 +#define CommandCodec 30 +#define CommandLevel 31 +#define CommandRaw 32 +#define CommandRawFormat 33 +#define CommandNetListen 34 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width . Default 1920", 1 }, + { CommandHeight, "-height", "h", "Set image height . Default 1080", 1 }, + { CommandBitrate, "-bitrate", "b", "Set bitrate. Use bits per second (e.g. 10MBits/s would be -b 10000000)", 1 }, + { CommandOutput, "-output", "o", "Output filename (to write to stdout, use '-o -').\n" + "\t\t Connect to a remote IPv4 host (e.g. tcp://192.168.1.2:1234, udp://192.168.1.2:1234)\n" + "\t\t To listen on a TCP port (IPv4) and wait for an incoming connection use -l\n" + "\t\t (e.g. raspivid -l -o tcp://0.0.0.0:3333 -> bind to all network interfaces, raspivid -l -o tcp://192.168.1.1:3333 -> bind to a certain local IPv4)", 1 }, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 }, + { CommandDemoMode, "-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1}, + { CommandFramerate, "-framerate", "fps","Specify the frames per second to record", 1}, + { CommandPreviewEnc, "-penc", "e", "Display preview image *after* encoding (shows compression artifacts)", 0}, + { CommandIntraPeriod, "-intra", "g", "Specify the intra refresh period (key frame rate/GoP size). Zero to produce an initial I-frame and then just P-frames.", 1}, + { CommandProfile, "-profile", "pf", "Specify H264 profile to use for encoding", 1}, + { CommandTimed, "-timed", "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0}, + { CommandSignal, "-signal", "s", "Cycle between capture and pause on Signal", 0}, + { CommandKeypress, "-keypress", "k", "Cycle between capture and pause on ENTER", 0}, + { CommandInitialState, "-initial", "i", "Initial state. Use 'record' or 'pause'. Default 'record'", 1}, + { CommandQP, "-qp", "qp", "Quantisation parameter. Use approximately 10-40. Default 0 (off)", 1}, + { CommandInlineHeaders, "-inline", "ih", "Insert inline headers (SPS, PPS) to stream", 0}, + { CommandSegmentFile, "-segment", "sg", "Segment output file in to multiple files at specified interval ", 1}, + { CommandSegmentWrap, "-wrap", "wr", "In segment mode, wrap any numbered filename back to 1 when reach number", 1}, + { CommandSegmentStart, "-start", "sn", "In segment mode, start with specified segment number", 1}, + { CommandSplitWait, "-split", "sp", "In wait mode, create new output file for each start event", 0}, + { CommandCircular, "-circular", "c", "Run encoded data through circular buffer until triggered then save", 0}, + { CommandIMV, "-vectors", "x", "Output filename for inline motion vectors", 1 }, + { CommandCamSelect, "-camselect", "cs", "Select camera . Default 0", 1 }, + { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0}, + { CommandSensorMode, "-mode", "md", "Force sensor mode. 0=auto. See docs for other modes available", 1}, + { CommandIntraRefreshType,"-irefresh", "if", "Set intra refresh type", 1}, + { CommandFlush, "-flush", "fl", "Flush buffers in order to decrease latency", 0 }, + { CommandSavePTS, "-save-pts", "pts","Save Timestamps to file for mkvmerge", 1 }, + { CommandCodec, "-codec", "cd", "Specify the codec to use - H264 (default) or MJPEG", 1 }, + { CommandLevel, "-level", "lev","Specify H264 level to use for encoding", 1}, + { CommandRaw, "-raw", "r", "Output filename for raw video", 1 }, + { CommandRawFormat, "-raw-format", "rf", "Specify output format for raw video. Default is yuv", 1}, + { CommandNetListen, "-listen", "l", "Listen on a TCP socket", 0}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + + +static struct +{ + char *description; + int nextWaitMethod; +} wait_method_description[] = +{ + {"Simple capture", WAIT_METHOD_NONE}, + {"Capture forever", WAIT_METHOD_FOREVER}, + {"Cycle on time", WAIT_METHOD_TIMED}, + {"Cycle on keypress", WAIT_METHOD_KEYPRESS}, + {"Cycle on signal", WAIT_METHOD_SIGNAL}, +}; + +static int wait_method_description_size = sizeof(wait_method_description) / sizeof(wait_method_description[0]); + + + +/** + * Assign a default set of parameters to the state passed in + * + * @param state Pointer to state structure to assign defaults to + */ +static void default_status(RASPIVID_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + // Default everything to zero + memset(state, 0, sizeof(RASPIVID_STATE)); + + // Now set anything non-zero + state->timeout = 5000; // 5s delay before take image + state->width = 1920; // Default to 1080p + state->height = 1080; + state->encoding = MMAL_ENCODING_H264; + state->bitrate = 17000000; // This is a decent default bitrate for 1080p + state->framerate = VIDEO_FRAME_RATE_NUM; + state->intraperiod = -1; // Not set + state->quantisationParameter = 0; + state->demoMode = 0; + state->demoInterval = 250; // ms + state->immutableInput = 1; + state->profile = MMAL_VIDEO_PROFILE_H264_HIGH; + state->level = MMAL_VIDEO_LEVEL_H264_4; + state->waitMethod = WAIT_METHOD_NONE; + state->onTime = 5000; + state->offTime = 5000; + + state->bCapturing = 0; + state->bInlineHeaders = 0; + + state->segmentSize = 0; // 0 = not segmenting the file. + state->segmentNumber = 1; + state->segmentWrap = 0; // Point at which to wrap segment number back to 1. 0 = no wrap + state->splitNow = 0; + state->splitWait = 0; + + state->inlineMotionVectors = 0; + state->cameraNum = 0; + state->settings = 0; + state->sensor_mode = 0; + + state->intra_refresh_type = -1; + + state->frame = 0; + state->save_pts = 0; + + state->netListen = false; + + + // Setup preview window defaults + raspipreview_set_defaults(&state->preview_parameters); + + // Set up the camera_parameters to default + raspicamcontrol_set_defaults(&state->camera_parameters); +} + + +/** + * Dump image state parameters to stderr. + * + * @param state Pointer to state structure to assign defaults to + */ +static void dump_status(RASPIVID_STATE *state) +{ + int i; + + if (!state) + { + vcos_assert(0); + return; + } + + fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, state->height, state->filename); + fprintf(stderr, "bitrate %d, framerate %d, time delay %d\n", state->bitrate, state->framerate, state->timeout); + fprintf(stderr, "H264 Profile %s\n", raspicli_unmap_xref(state->profile, profile_map, profile_map_size)); + fprintf(stderr, "H264 Level %s\n", raspicli_unmap_xref(state->level, level_map, level_map_size)); + fprintf(stderr, "H264 Quantisation level %d, Inline headers %s\n", state->quantisationParameter, state->bInlineHeaders ? "Yes" : "No"); + fprintf(stderr, "H264 Intra refresh type %s, period %d\n", raspicli_unmap_xref(state->intra_refresh_type, intra_refresh_map, intra_refresh_map_size), state->intraperiod); + + // Not going to display segment data unless asked for it. + if (state->segmentSize) + fprintf(stderr, "Segment size %d, segment wrap value %d, initial segment number %d\n", state->segmentSize, state->segmentWrap, state->segmentNumber); + + if (state->raw_output) + fprintf(stderr, "Raw output enabled, format %s\n", raspicli_unmap_xref(state->raw_output_fmt, raw_output_fmt_map, raw_output_fmt_map_size)); + + fprintf(stderr, "Wait method : "); + for (i=0;iwaitMethod == wait_method_description[i].nextWaitMethod) + fprintf(stderr, "%s", wait_method_description[i].description); + } + fprintf(stderr, "\nInitial state '%s'\n", raspicli_unmap_xref(state->bCapturing, initial_map, initial_map_size)); + fprintf(stderr, "\n\n"); + + raspipreview_dump_parameters(&state->preview_parameters); + raspicamcontrol_dump_parameters(&state->camera_parameters); +} + +/** + * Parse the incoming command line and put resulting parameters in to the state + * + * @param argc Number of arguments in command line + * @param argv Array of pointers to strings from command line + * @param state Pointer to state structure to assign any discovered parameters to + * @return Non-0 if failed for some reason, 0 otherwise + */ +static int parse_cmdline(int argc, const char **argv, RASPIVID_STATE *state) +{ + // Parse the command line arguments. + // We are looking for -- or - + + int valid = 1; + int i; + + for (i = 1; i < argc && valid; i++) + { + int command_id, num_parameters; + + if (!argv[i]) + continue; + + if (argv[i][0] != '-') + { + valid = 0; + continue; + } + + // Assume parameter is valid until proven otherwise + valid = 1; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters); + + // If we found a command but are missing a parameter, continue (and we will drop out of the loop) + if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) ) + continue; + + // We are now dealing with a command line option + switch (command_id) + { + case CommandHelp: + display_valid_parameters(basename(argv[0])); + return -1; + + case CommandWidth: // Width > 0 + if (sscanf(argv[i + 1], "%u", &state->width) != 1) + valid = 0; + else + i++; + break; + + case CommandHeight: // Height > 0 + if (sscanf(argv[i + 1], "%u", &state->height) != 1) + valid = 0; + else + i++; + break; + + case CommandBitrate: // 1-100 + if (sscanf(argv[i + 1], "%u", &state->bitrate) == 1) + { + i++; + } + else + valid = 0; + + break; + + case CommandOutput: // output filename + { + int len = strlen(argv[i + 1]); + if (len) + { + state->filename = malloc(len + 1); + vcos_assert(state->filename); + if (state->filename) + strncpy(state->filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandVerbose: // display lots of data during run + state->verbose = 1; + break; + + case CommandTimeout: // Time to run viewfinder/capture + { + if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) + { + // Ensure that if previously selected a waitMethod we don't overwrite it + if (state->timeout == 0 && state->waitMethod == WAIT_METHOD_NONE) + state->waitMethod = WAIT_METHOD_FOREVER; + + i++; + } + else + valid = 0; + break; + } + + case CommandDemoMode: // Run in demo mode - no capture + { + // Demo mode might have a timing parameter + // so check if a) we have another parameter, b) its not the start of the next option + if (i + 1 < argc && argv[i+1][0] != '-') + { + if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1) + { + // TODO : What limits do we need for timeout? + if (state->demoInterval == 0) + state->demoInterval = 250; // ms + + state->demoMode = 1; + i++; + } + else + valid = 0; + } + else + { + state->demoMode = 1; + } + + break; + } + + case CommandFramerate: // fps to record + { + if (sscanf(argv[i + 1], "%u", &state->framerate) == 1) + { + // TODO : What limits do we need for fps 1 - 30 - 120?? + i++; + } + else + valid = 0; + break; + } + + case CommandPreviewEnc: + state->immutableInput = 0; + break; + + case CommandIntraPeriod: // key frame rate + { + if (sscanf(argv[i + 1], "%u", &state->intraperiod) == 1) + i++; + else + valid = 0; + break; + } + + case CommandQP: // quantisation parameter + { + if (sscanf(argv[i + 1], "%u", &state->quantisationParameter) == 1) + i++; + else + valid = 0; + break; + } + + case CommandProfile: // H264 profile + { + state->profile = raspicli_map_xref(argv[i + 1], profile_map, profile_map_size); + + if( state->profile == -1) + state->profile = MMAL_VIDEO_PROFILE_H264_HIGH; + + i++; + break; + } + + case CommandInlineHeaders: // H264 inline headers + { + state->bInlineHeaders = 1; + break; + } + + case CommandTimed: + { + if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2) + { + i++; + + if (state->onTime < 1000) + state->onTime = 1000; + + if (state->offTime < 1000) + state->offTime = 1000; + + state->waitMethod = WAIT_METHOD_TIMED; + } + else + valid = 0; + break; + } + + case CommandKeypress: + state->waitMethod = WAIT_METHOD_KEYPRESS; + break; + + case CommandSignal: + state->waitMethod = WAIT_METHOD_SIGNAL; + // Reenable the signal + signal(SIGUSR1, signal_handler); + break; + + case CommandInitialState: + { + state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size); + + if( state->bCapturing == -1) + state->bCapturing = 0; + + i++; + break; + } + + case CommandSegmentFile: // Segment file in to chunks of specified time + { + if (sscanf(argv[i + 1], "%u", &state->segmentSize) == 1) + { + // Must enable inline headers for this to work + state->bInlineHeaders = 1; + i++; + } + else + valid = 0; + break; + } + + case CommandSegmentWrap: // segment wrap value + { + if (sscanf(argv[i + 1], "%u", &state->segmentWrap) == 1) + i++; + else + valid = 0; + break; + } + + case CommandSegmentStart: // initial segment number + { + if((sscanf(argv[i + 1], "%u", &state->segmentNumber) == 1) && (!state->segmentWrap || (state->segmentNumber <= state->segmentWrap))) + i++; + else + valid = 0; + break; + } + + case CommandSplitWait: // split files on restart + { + // Must enable inline headers for this to work + state->bInlineHeaders = 1; + state->splitWait = 1; + break; + } + + case CommandCircular: + { + state->bCircularBuffer = 1; + break; + } + + case CommandIMV: // output filename + { + state->inlineMotionVectors = 1; + int len = strlen(argv[i + 1]); + if (len) + { + state->imv_filename = malloc(len + 1); + vcos_assert(state->imv_filename); + if (state->imv_filename) + strncpy(state->imv_filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + case CommandCamSelect: //Select camera input port + { + if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandSettings: + state->settings = 1; + break; + + case CommandSensorMode: + { + if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandIntraRefreshType: + { + state->intra_refresh_type = raspicli_map_xref(argv[i + 1], intra_refresh_map, intra_refresh_map_size); + i++; + break; + } + + case CommandFlush: + { + state->callback_data.flush_buffers = 1; + break; + } + case CommandSavePTS: // output filename + { + state->save_pts = 1; + int len = strlen(argv[i + 1]); + if (len) + { + state->pts_filename = malloc(len + 1); + vcos_assert(state->pts_filename); + if (state->pts_filename) + strncpy(state->pts_filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + case CommandCodec: // codec type + { + int len = strlen(argv[i + 1]); + if (len) + { + if (len==4 && !strncmp("H264", argv[i+1], 4)) + state->encoding = MMAL_ENCODING_H264; + else if (len==5 && !strncmp("MJPEG", argv[i+1], 5)) + state->encoding = MMAL_ENCODING_MJPEG; + else + valid = 0; + i++; + } + else + valid = 0; + break; + } + + case CommandLevel: // H264 level + { + state->level = raspicli_map_xref(argv[i + 1], level_map, level_map_size); + + if( state->level == -1) + state->level = MMAL_VIDEO_LEVEL_H264_4; + + i++; + break; + } + + case CommandRaw: // output filename + { + state->raw_output = 1; + state->raw_output_fmt = RAW_OUTPUT_FMT_YUV; + int len = strlen(argv[i + 1]); + if (len) + { + state->raw_filename = malloc(len + 1); + vcos_assert(state->raw_filename); + if (state->raw_filename) + strncpy(state->raw_filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandRawFormat: + { + state->raw_output_fmt = raspicli_map_xref(argv[i + 1], raw_output_fmt_map, raw_output_fmt_map_size); + + if (state->raw_output_fmt == -1) + valid = 0; + + i++; + break; + } + + case CommandNetListen: + { + state->netListen = true; + + break; + } + + default: + { + // Try parsing for any image specific parameters + // result indicates how many parameters were used up, 0,1,2 + // but we adjust by -1 as we have used one already + const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL; + int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg)); + + // Still unused, try preview options + if (!parms_used) + parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + + + // If no parms were used, this must be a bad parameters + if (!parms_used) + valid = 0; + else + i += parms_used - 1; + + break; + } + } + } + + if (!valid) + { + fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]); + return 1; + } + + // Always disable verbose if output going to stdout + if (state->filename && state->filename[0] == '-') + { + state->verbose = 0; + } + + return 0; +} + +/** + * Display usage information for the application to stdout + * + * @param app_name String to display as the application name + */ +static void display_valid_parameters(char *app_name) +{ + int i; + + fprintf(stdout, "Display camera output to display, and optionally saves an H264 capture at requested bitrate\n\n"); + fprintf(stdout, "\nusage: %s [options]\n\n", app_name); + + fprintf(stdout, "Image parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + // Profile options + fprintf(stdout, "\n\nH264 Profile options :\n%s", profile_map[0].mode ); + + for (i=1;icmd == MMAL_EVENT_PARAMETER_CHANGED) + { + MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data; + switch (param->hdr.id) { + case MMAL_PARAMETER_CAMERA_SETTINGS: + { + MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param; + vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u", + settings->exposure, + settings->analog_gain.num, settings->analog_gain.den, + settings->digital_gain.num, settings->digital_gain.den); + vcos_log_error("AWB R=%u/%u, B=%u/%u", + settings->awb_red_gain.num, settings->awb_red_gain.den, + settings->awb_blue_gain.num, settings->awb_blue_gain.den + ); + } + break; + } + } + else if (buffer->cmd == MMAL_EVENT_ERROR) + { + vcos_log_error("No data received from sensor. Check all connections, including the Sunny one on the camera board"); + } + else + { + vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); + } + + mmal_buffer_header_release(buffer); +} + + +/** + * Open a file based on the settings in state + * + * @param state Pointer to state + */ +static FILE *open_filename(RASPIVID_STATE *pState, char *filename) +{ + FILE *new_handle = NULL; + char *tempname = NULL; + + if (pState->segmentSize || pState->splitWait) + { + // Create a new filename string + asprintf(&tempname, filename, pState->segmentNumber); + filename = tempname; + } + + if (filename) + { + bool bNetwork = false; + int sfd = -1, socktype; + + if(!strncmp("tcp://", filename, 6)) + { + bNetwork = true; + socktype = SOCK_STREAM; + } + else if(!strncmp("udp://", filename, 6)) + { + if (pState->netListen) + { + fprintf(stderr, "No support for listening in UDP mode\n"); + exit(131); + } + bNetwork = true; + socktype = SOCK_DGRAM; + } + + if(bNetwork) + { + unsigned short port; + filename += 6; + char *colon; + if(NULL == (colon = strchr(filename, ':'))) + { + fprintf(stderr, "%s is not a valid IPv4:port, use something like tcp://1.2.3.4:1234 or udp://1.2.3.4:1234\n", + filename); + exit(132); + } + if(1 != sscanf(colon + 1, "%hu", &port)) + { + fprintf(stderr, + "Port parse failed. %s is not a valid network file name, use something like tcp://1.2.3.4:1234 or udp://1.2.3.4:1234\n", + filename); + exit(133); + } + char chTmp = *colon; + *colon = 0; + + struct sockaddr_in saddr={}; + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + if(0 == inet_aton(filename, &saddr.sin_addr)) + { + fprintf(stderr, "inet_aton failed. %s is not a valid IPv4 address\n", + filename); + exit(134); + } + *colon = chTmp; + + if (pState->netListen) + { + int sockListen = socket(AF_INET, SOCK_STREAM, 0); + if (sockListen >= 0) + { + int iTmp = 1; + setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &iTmp, sizeof(int));//no error handling, just go on + if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0) + { + while ((-1 == (iTmp = listen(sockListen, 0))) && (EINTR == errno)) + ; + if (-1 != iTmp) + { + fprintf(stderr, "Waiting for a TCP connection on %s:%"SCNu16"...", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + struct sockaddr_in cli_addr; + socklen_t clilen = sizeof(cli_addr); + while ((-1 == (sfd = accept(sockListen, (struct sockaddr *) &cli_addr, &clilen))) && (EINTR == errno)) + ; + if (sfd >= 0) + fprintf(stderr, "Client connected from %s:%"SCNu16"\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); + else + fprintf(stderr, "Error on accept: %s\n", strerror(errno)); + } + else//if (-1 != iTmp) + { + fprintf(stderr, "Error trying to listen on a socket: %s\n", strerror(errno)); + } + } + else//if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0) + { + fprintf(stderr, "Error on binding socket: %s\n", strerror(errno)); + } + } + else//if (sockListen >= 0) + { + fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); + } + + if (sockListen >= 0)//regardless success or error + close(sockListen);//do not listen on a given port anymore + } + else//if (pState->netListen) + { + if(0 <= (sfd = socket(AF_INET, socktype, 0))) + { + fprintf(stderr, "Connecting to %s:%hu...", inet_ntoa(saddr.sin_addr), port); + + int iTmp = 1; + while ((-1 == (iTmp = connect(sfd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in)))) && (EINTR == errno)) + ; + if (iTmp < 0) + fprintf(stderr, "error: %s\n", strerror(errno)); + else + fprintf(stderr, "connected, sending video...\n"); + } + else + fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); + } + + if (sfd >= 0) + new_handle = fdopen(sfd, "w"); + } + else + { + new_handle = fopen(filename, "wb"); + } + } + + if (pState->verbose) + { + if (new_handle) + fprintf(stderr, "Opening output file \"%s\"\n", filename); + else + fprintf(stderr, "Failed to open new file \"%s\"\n", filename); + } + + if (tempname) + free(tempname); + + return new_handle; +} + +/** + * Update any annotation data specific to the video. + * This simply passes on the setting from cli, or + * if application defined annotate requested, updates + * with the H264 parameters + * + * @param state Pointer to state control struct + * + */ +static void update_annotation_data(RASPIVID_STATE *state) +{ + // So, if we have asked for a application supplied string, set it to the H264 parameters + if (state->camera_parameters.enable_annotate & ANNOTATE_APP_TEXT) + { + char *text; + const char *refresh = raspicli_unmap_xref(state->intra_refresh_type, intra_refresh_map, intra_refresh_map_size); + + asprintf(&text, "%dk,%df,%s,%d,%s,%s", + state->bitrate / 1000, state->framerate, + refresh ? refresh : "(none)", + state->intraperiod, + raspicli_unmap_xref(state->profile, profile_map, profile_map_size), + raspicli_unmap_xref(state->level, level_map, level_map_size)); + + raspicamcontrol_set_annotate(state->camera_component, state->camera_parameters.enable_annotate, text, + state->camera_parameters.annotate_text_size, + state->camera_parameters.annotate_text_colour, + state->camera_parameters.annotate_bg_colour); + + free(text); + } + else + { + raspicamcontrol_set_annotate(state->camera_component, state->camera_parameters.enable_annotate, state->camera_parameters.annotate_string, + state->camera_parameters.annotate_text_size, + state->camera_parameters.annotate_text_colour, + state->camera_parameters.annotate_bg_colour); + } +} + + + +/** + * buffer header callback function for encoder + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_BUFFER_HEADER_T *new_buffer; + static int64_t base_time = -1; + static int64_t last_second = -1; + + // All our segment times based on the receipt of the first encoder callback + if (base_time == -1) + base_time = vcos_getmicrosecs64()/1000; + + // We pass our file handle and other stuff in via the userdata field. + + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = buffer->length; + int64_t current_time = vcos_getmicrosecs64()/1000; + + vcos_assert(pData->file_handle); + if(pData->pstate->inlineMotionVectors) vcos_assert(pData->imv_file_handle); + + if (pData->cb_buff) + { + int space_in_buff = pData->cb_len - pData->cb_wptr; + int copy_to_end = space_in_buff > buffer->length ? buffer->length : space_in_buff; + int copy_to_start = buffer->length - copy_to_end; + + if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG) + { + if(pData->header_wptr + buffer->length > sizeof(pData->header_bytes)) + { + vcos_log_error("Error in header bytes\n"); + } + else + { + // These are the header bytes, save them for final output + mmal_buffer_header_mem_lock(buffer); + memcpy(pData->header_bytes + pData->header_wptr, buffer->data, buffer->length); + mmal_buffer_header_mem_unlock(buffer); + pData->header_wptr += buffer->length; + } + } + else if((buffer->flags & MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO)) + { + // Do something with the inline motion vectors... + } + else + { + static int frame_start = -1; + int i; + + if(frame_start == -1) + frame_start = pData->cb_wptr; + + if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME) + { + pData->iframe_buff[pData->iframe_buff_wpos] = frame_start; + pData->iframe_buff_wpos = (pData->iframe_buff_wpos + 1) % IFRAME_BUFSIZE; + } + + if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) + frame_start = -1; + + // If we overtake the iframe rptr then move the rptr along + if((pData->iframe_buff_rpos + 1) % IFRAME_BUFSIZE != pData->iframe_buff_wpos) + { + while( + ( + pData->cb_wptr <= pData->iframe_buff[pData->iframe_buff_rpos] && + (pData->cb_wptr + buffer->length) > pData->iframe_buff[pData->iframe_buff_rpos] + ) || + ( + (pData->cb_wptr > pData->iframe_buff[pData->iframe_buff_rpos]) && + (pData->cb_wptr + buffer->length) > (pData->iframe_buff[pData->iframe_buff_rpos] + pData->cb_len) + ) + ) + pData->iframe_buff_rpos = (pData->iframe_buff_rpos + 1) % IFRAME_BUFSIZE; + } + + mmal_buffer_header_mem_lock(buffer); + // We are pushing data into a circular buffer + memcpy(pData->cb_buff + pData->cb_wptr, buffer->data, copy_to_end); + memcpy(pData->cb_buff, buffer->data + copy_to_end, copy_to_start); + mmal_buffer_header_mem_unlock(buffer); + + if((pData->cb_wptr + buffer->length) > pData->cb_len) + pData->cb_wrap = 1; + + pData->cb_wptr = (pData->cb_wptr + buffer->length) % pData->cb_len; + + for(i = pData->iframe_buff_rpos; i != pData->iframe_buff_wpos; i = (i + 1) % IFRAME_BUFSIZE) + { + int p = pData->iframe_buff[i]; + if(pData->cb_buff[p] != 0 || pData->cb_buff[p+1] != 0 || pData->cb_buff[p+2] != 0 || pData->cb_buff[p+3] != 1) + { + vcos_log_error("Error in iframe list\n"); + } + } + } + } + else + { + // For segmented record mode, we need to see if we have exceeded our time/size, + // but also since we have inline headers turned on we need to break when we get one to + // ensure that the new stream has the header in it. If we break on an I-frame, the + // SPS/PPS header is actually in the previous chunk. + if ((buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG) && + ((pData->pstate->segmentSize && current_time > base_time + pData->pstate->segmentSize) || + (pData->pstate->splitWait && pData->pstate->splitNow))) + { + FILE *new_handle; + + base_time = current_time; + + pData->pstate->splitNow = 0; + pData->pstate->segmentNumber++; + + // Only wrap if we have a wrap point set + if (pData->pstate->segmentWrap && pData->pstate->segmentNumber > pData->pstate->segmentWrap) + pData->pstate->segmentNumber = 1; + + if (pData->pstate->filename && pData->pstate->filename[0] != '-') + { + new_handle = open_filename(pData->pstate, pData->pstate->filename); + + if (new_handle) + { + fclose(pData->file_handle); + pData->file_handle = new_handle; + } + } + + if (pData->pstate->imv_filename && pData->pstate->imv_filename[0] != '-') + { + new_handle = open_filename(pData->pstate, pData->pstate->imv_filename); + + if (new_handle) + { + fclose(pData->imv_file_handle); + pData->imv_file_handle = new_handle; + } + } + + if (pData->pstate->pts_filename && pData->pstate->pts_filename[0] != '-') + { + new_handle = open_filename(pData->pstate, pData->pstate->pts_filename); + + if (new_handle) + { + fclose(pData->pts_file_handle); + pData->pts_file_handle = new_handle; + } + } + } + if (buffer->length) + { + mmal_buffer_header_mem_lock(buffer); + if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO) + { + if(pData->pstate->inlineMotionVectors) + { + bytes_written = fwrite(buffer->data, 1, buffer->length, pData->imv_file_handle); + if(pData->flush_buffers) fflush(pData->imv_file_handle); + } + else + { + //We do not want to save inlineMotionVectors... + bytes_written = buffer->length; + } + } + else + { + bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle); + if(pData->flush_buffers) fflush(pData->file_handle); + + if(pData->pstate->save_pts && + (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END || + buffer->flags == 0 || + buffer->flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME) && + !(buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG)) + { + if(buffer->pts != MMAL_TIME_UNKNOWN && buffer->pts != pData->pstate->lasttime) + { + int64_t pts; + if(pData->pstate->frame==0)pData->pstate->starttime=buffer->pts; + pData->pstate->lasttime=buffer->pts; + pts = buffer->pts - pData->pstate->starttime; + fprintf(pData->pts_file_handle,"%lld.%03lld\n", pts/1000, pts%1000); + pData->pstate->frame++; + } + } + } + + mmal_buffer_header_mem_unlock(buffer); + + if (bytes_written != buffer->length) + { + vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, buffer->length); + pData->abort = 1; + } + } + } + + // See if the second count has changed and we need to update any annotation + if (current_time/1000 != last_second) + { + update_annotation_data(pData->pstate); + last_second = current_time/1000; + } + } + else + { + vcos_log_error("Received a encoder buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status; + + new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue); + + if (new_buffer) + status = mmal_port_send_buffer(port, new_buffer); + + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return a buffer to the encoder port"); + } +} + +/** + * buffer header callback function for splitter + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void splitter_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_BUFFER_HEADER_T *new_buffer; + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = 0; + int bytes_to_write = buffer->length; + + /* Write only luma component to get grayscale image: */ + if (buffer->length && pData->pstate->raw_output_fmt == RAW_OUTPUT_FMT_GRAY) + bytes_to_write = port->format->es->video.width * port->format->es->video.height; + + vcos_assert(pData->raw_file_handle); + + if (bytes_to_write) + { + mmal_buffer_header_mem_lock(buffer); + bytes_written = fwrite(buffer->data, 1, bytes_to_write, pData->raw_file_handle); + mmal_buffer_header_mem_unlock(buffer); + + if (bytes_written != bytes_to_write) + { + vcos_log_error("Failed to write raw buffer data (%d from %d)- aborting", bytes_written, bytes_to_write); + pData->abort = 1; + } + } + } + else + { + vcos_log_error("Received a camera buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status; + + new_buffer = mmal_queue_get(pData->pstate->splitter_pool->queue); + + if (new_buffer) + status = mmal_port_send_buffer(port, new_buffer); + + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return a buffer to the splitter port"); + } +} + +/** + * Create the camera component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_camera_component(RASPIVID_STATE *state) +{ + MMAL_COMPONENT_T *camera = 0; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_STATUS_T status; + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create camera component"); + goto error; + } + + status = raspicamcontrol_set_stereo_mode(camera->output[0], &state->camera_parameters.stereo_mode); + status += raspicamcontrol_set_stereo_mode(camera->output[1], &state->camera_parameters.stereo_mode); + status += raspicamcontrol_set_stereo_mode(camera->output[2], &state->camera_parameters.stereo_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set stereo mode : error %d", status); + goto error; + } + + MMAL_PARAMETER_INT32_T camera_num = + {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, state->cameraNum}; + + status = mmal_port_parameter_set(camera->control, &camera_num.hdr); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not select camera : error %d", status); + goto error; + } + + if (!camera->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera doesn't have output ports"); + goto error; + } + + status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, state->sensor_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set sensor mode : error %d", status); + goto error; + } + + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + if (state->settings) + { + MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request = + {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)}, + MMAL_PARAMETER_CAMERA_SETTINGS, 1}; + + status = mmal_port_parameter_set(camera->control, &change_event_request.hdr); + if ( status != MMAL_SUCCESS ) + { + vcos_log_error("No camera settings events"); + } + } + + // Enable the camera, and tell it its control callback function + status = mmal_port_enable(camera->control, camera_control_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable control port : error %d", status); + goto error; + } + + // set up the camera configuration + { + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = + { + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->width, + .max_stills_h = state->height, + .stills_yuv422 = 0, + .one_shot_stills = 0, + .max_preview_video_w = state->width, + .max_preview_video_h = state->height, + .num_preview_video_frames = 3 + vcos_max(0, (state->framerate-30)/10), + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC + }; + mmal_port_parameter_set(camera->control, &cam_config.hdr); + } + + // Now set up the port formats + + // Set the encode format on the Preview port + // HW limitations mean we need the preview to be the same size as the required recorded output + + format = preview_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 166, 1000 }, {999, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + + //enable dynamic framerate if necessary + if (state->camera_parameters.shutter_speed) + { + if (state->framerate > 1000000./state->camera_parameters.shutter_speed) + { + state->framerate=0; + if (state->verbose) + fprintf(stderr, "Enable dynamic frame rate to fulfil shutter speed requirement\n"); + } + } + + format->encoding = MMAL_ENCODING_OPAQUE; + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; + + status = mmal_port_format_commit(preview_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto error; + } + + // Set the encode format on the video port + + format = video_port->format; + format->encoding_variant = MMAL_ENCODING_I420; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 167, 1000 }, {999, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + + format->encoding = MMAL_ENCODING_OPAQUE; + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = state->framerate; + format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; + + status = mmal_port_format_commit(video_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera video format couldn't be set"); + goto error; + } + + // Ensure there are enough buffers to avoid dropping frames + if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + + // Set the encode format on the still port + + format = still_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = 0; + format->es->video.frame_rate.den = 1; + + status = mmal_port_format_commit(still_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera still format couldn't be set"); + goto error; + } + + /* Ensure there are enough buffers to avoid dropping frames */ + if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + /* Enable component */ + status = mmal_component_enable(camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera component couldn't be enabled"); + goto error; + } + + // Note: this sets lots of parameters that were not individually addressed before. + raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + + state->camera_component = camera; + + update_annotation_data(state); + + if (state->verbose) + fprintf(stderr, "Camera component done\n"); + + return status; + +error: + + if (camera) + mmal_component_destroy(camera); + + return status; +} + +/** + * Destroy the camera component + * + * @param state Pointer to state control struct + * + */ +static void destroy_camera_component(RASPIVID_STATE *state) +{ + if (state->camera_component) + { + mmal_component_destroy(state->camera_component); + state->camera_component = NULL; + } +} + +/** + * Create the splitter component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_splitter_component(RASPIVID_STATE *state) +{ + MMAL_COMPONENT_T *splitter = 0; + MMAL_PORT_T *splitter_output = NULL; + MMAL_ES_FORMAT_T *format; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + int i; + + if (state->camera_component == NULL) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera component must be created before splitter"); + goto error; + } + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_SPLITTER, &splitter); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create splitter component"); + goto error; + } + + if (!splitter->input_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Splitter doesn't have any input port"); + goto error; + } + + if (splitter->output_num < 2) + { + status = MMAL_ENOSYS; + vcos_log_error("Splitter doesn't have enough output ports"); + goto error; + } + + /* Ensure there are enough buffers to avoid dropping frames: */ + mmal_format_copy(splitter->input[0]->format, state->camera_component->output[MMAL_CAMERA_PREVIEW_PORT]->format); + + if (splitter->input[0]->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + splitter->input[0]->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + status = mmal_port_format_commit(splitter->input[0]); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set format on splitter input port"); + goto error; + } + + /* Splitter can do format conversions, configure format for its output port: */ + for (i = 0; i < splitter->output_num; i++) + { + mmal_format_copy(splitter->output[i]->format, splitter->input[0]->format); + + if (i == SPLITTER_OUTPUT_PORT) + { + format = splitter->output[i]->format; + + switch (state->raw_output_fmt) + { + case RAW_OUTPUT_FMT_YUV: + case RAW_OUTPUT_FMT_GRAY: /* Grayscale image contains only luma (Y) component */ + format->encoding = MMAL_ENCODING_I420; + format->encoding_variant = MMAL_ENCODING_I420; + break; + case RAW_OUTPUT_FMT_RGB: + if (mmal_util_rgb_order_fixed(state->camera_component->output[MMAL_CAMERA_CAPTURE_PORT])) + format->encoding = MMAL_ENCODING_RGB24; + else + format->encoding = MMAL_ENCODING_BGR24; + format->encoding_variant = 0; /* Irrelevant when not in opaque mode */ + break; + default: + status = MMAL_EINVAL; + vcos_log_error("unknown raw output format"); + goto error; + } + } + + status = mmal_port_format_commit(splitter->output[i]); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set format on splitter output port %d", i); + goto error; + } + } + + /* Enable component */ + status = mmal_component_enable(splitter); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("splitter component couldn't be enabled"); + goto error; + } + + /* Create pool of buffer headers for the output port to consume */ + splitter_output = splitter->output[SPLITTER_OUTPUT_PORT]; + pool = mmal_port_pool_create(splitter_output, splitter_output->buffer_num, splitter_output->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for splitter output port %s", splitter_output->name); + } + + state->splitter_pool = pool; + state->splitter_component = splitter; + + if (state->verbose) + fprintf(stderr, "Splitter component done\n"); + + return status; + +error: + + if (splitter) + mmal_component_destroy(splitter); + + return status; +} + +/** + * Destroy the splitter component + * + * @param state Pointer to state control struct + * + */ +static void destroy_splitter_component(RASPIVID_STATE *state) +{ + // Get rid of any port buffers first + if (state->splitter_pool) + { + mmal_port_pool_destroy(state->splitter_component->output[SPLITTER_OUTPUT_PORT], state->splitter_pool); + } + + if (state->splitter_component) + { + mmal_component_destroy(state->splitter_component); + state->splitter_component = NULL; + } +} + +/** + * Create the encoder component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_encoder_component(RASPIVID_STATE *state) +{ + MMAL_COMPONENT_T *encoder = 0; + MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, &encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to create video encoder component"); + goto error; + } + + if (!encoder->input_num || !encoder->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Video encoder doesn't have input/output ports"); + goto error; + } + + encoder_input = encoder->input[0]; + encoder_output = encoder->output[0]; + + // We want same format on input and output + mmal_format_copy(encoder_output->format, encoder_input->format); + + // Only supporting H264 at the moment + encoder_output->format->encoding = state->encoding; + + if(state->encoding == MMAL_ENCODING_H264) + { + if(state->level == MMAL_VIDEO_LEVEL_H264_4) + { + if(state->bitrate > MAX_BITRATE_LEVEL4) + { + fprintf(stderr, "Bitrate too high: Reducing to 25MBit/s\n"); + state->bitrate = MAX_BITRATE_LEVEL4; + } + } + else + { + if(state->bitrate > MAX_BITRATE_LEVEL42) + { + fprintf(stderr, "Bitrate too high: Reducing to 62.5MBit/s\n"); + state->bitrate = MAX_BITRATE_LEVEL42; + } + } + } + else if(state->encoding == MMAL_ENCODING_MJPEG) + { + if(state->bitrate > MAX_BITRATE_MJPEG) + { + fprintf(stderr, "Bitrate too high: Reducing to 25MBit/s\n"); + state->bitrate = MAX_BITRATE_MJPEG; + } + } + + encoder_output->format->bitrate = state->bitrate; + + if (state->encoding == MMAL_ENCODING_H264) + encoder_output->buffer_size = encoder_output->buffer_size_recommended; + else + encoder_output->buffer_size = 256<<10; + + + if (encoder_output->buffer_size < encoder_output->buffer_size_min) + encoder_output->buffer_size = encoder_output->buffer_size_min; + + encoder_output->buffer_num = encoder_output->buffer_num_recommended; + + if (encoder_output->buffer_num < encoder_output->buffer_num_min) + encoder_output->buffer_num = encoder_output->buffer_num_min; + + // We need to set the frame rate on output to 0, to ensure it gets + // updated correctly from the input framerate when port connected + encoder_output->format->es->video.frame_rate.num = 0; + encoder_output->format->es->video.frame_rate.den = 1; + + // Commit the port changes to the output port + status = mmal_port_format_commit(encoder_output); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set format on video encoder output port"); + goto error; + } + + // Set the rate control parameter + if (0) + { + MMAL_PARAMETER_VIDEO_RATECONTROL_T param = {{ MMAL_PARAMETER_RATECONTROL, sizeof(param)}, MMAL_VIDEO_RATECONTROL_DEFAULT}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set ratecontrol"); + goto error; + } + + } + + if (state->encoding == MMAL_ENCODING_H264 && + state->intraperiod != -1) + { + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_INTRAPERIOD, sizeof(param)}, state->intraperiod}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set intraperiod"); + goto error; + } + } + + if (state->encoding == MMAL_ENCODING_H264 && + state->quantisationParameter) + { + MMAL_PARAMETER_UINT32_T param = {{ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, sizeof(param)}, state->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set initial QP"); + goto error; + } + + MMAL_PARAMETER_UINT32_T param2 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, sizeof(param)}, state->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m2.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set min QP"); + goto error; + } + + MMAL_PARAMETER_UINT32_T param3 = {{ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, sizeof(param)}, state->quantisationParameter}; + status = mmal_port_parameter_set(encoder_output, ¶m3.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set max QP"); + goto error; + } + + } + + if (state->encoding == MMAL_ENCODING_H264) + { + MMAL_PARAMETER_VIDEO_PROFILE_T param; + param.hdr.id = MMAL_PARAMETER_PROFILE; + param.hdr.size = sizeof(param); + + param.profile[0].profile = state->profile; + + if((VCOS_ALIGN_UP(state->width,16) >> 4) * (VCOS_ALIGN_UP(state->height,16) >> 4) * state->framerate > 245760) + { + if((VCOS_ALIGN_UP(state->width,16) >> 4) * (VCOS_ALIGN_UP(state->height,16) >> 4) * state->framerate <= 522240) + { + fprintf(stderr, "Too many macroblocks/s: Increasing H264 Level to 4.2\n"); + state->level=MMAL_VIDEO_LEVEL_H264_42; + } + else + { + vcos_log_error("Too many macroblocks/s requested"); + goto error; + } + } + + param.profile[0].level = state->level; + + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set H264 profile"); + goto error; + } + } + + if (mmal_port_parameter_set_boolean(encoder_input, MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, state->immutableInput) != MMAL_SUCCESS) + { + vcos_log_error("Unable to set immutable input flag"); + // Continue rather than abort.. + } + + //set INLINE HEADER flag to generate SPS and PPS for every IDR if requested + if (mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, state->bInlineHeaders) != MMAL_SUCCESS) + { + vcos_log_error("failed to set INLINE HEADER FLAG parameters"); + // Continue rather than abort.. + } + + //set INLINE VECTORS flag to request motion vector estimates + if (state->encoding == MMAL_ENCODING_H264 && + mmal_port_parameter_set_boolean(encoder_output, MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, state->inlineMotionVectors) != MMAL_SUCCESS) + { + vcos_log_error("failed to set INLINE VECTORS parameters"); + // Continue rather than abort.. + } + + // Adaptive intra refresh settings + if (state->encoding == MMAL_ENCODING_H264 && + state->intra_refresh_type != -1) + { + MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T param; + param.hdr.id = MMAL_PARAMETER_VIDEO_INTRA_REFRESH; + param.hdr.size = sizeof(param); + + // Get first so we don't overwrite anything unexpectedly + status = mmal_port_parameter_get(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_warn("Unable to get existing H264 intra-refresh values. Please update your firmware"); + // Set some defaults, don't just pass random stack data + param.air_mbs = param.air_ref = param.cir_mbs = param.pir_mbs = 0; + } + + param.refresh_mode = state->intra_refresh_type; + + //if (state->intra_refresh_type == MMAL_VIDEO_INTRA_REFRESH_CYCLIC_MROWS) + // param.cir_mbs = 10; + + status = mmal_port_parameter_set(encoder_output, ¶m.hdr); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to set H264 intra-refresh values"); + goto error; + } + } + + // Enable component + status = mmal_component_enable(encoder); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable video encoder component"); + goto error; + } + + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name); + } + + state->encoder_pool = pool; + state->encoder_component = encoder; + + if (state->verbose) + fprintf(stderr, "Encoder component done\n"); + + return status; + + error: + if (encoder) + mmal_component_destroy(encoder); + + state->encoder_component = NULL; + + return status; +} + +/** + * Destroy the encoder component + * + * @param state Pointer to state control struct + * + */ +static void destroy_encoder_component(RASPIVID_STATE *state) +{ + // Get rid of any port buffers first + if (state->encoder_pool) + { + mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool); + } + + if (state->encoder_component) + { + mmal_component_destroy(state->encoder_component); + state->encoder_component = NULL; + } +} + +/** + * Connect two specific ports together + * + * @param output_port Pointer the output port + * @param input_port Pointer the input port + * @param Pointer to a mmal connection pointer, reassigned if function successful + * @return Returns a MMAL_STATUS_T giving result of operation + * + */ +static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) +{ + MMAL_STATUS_T status; + + status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); + + if (status == MMAL_SUCCESS) + { + status = mmal_connection_enable(*connection); + if (status != MMAL_SUCCESS) + mmal_connection_destroy(*connection); + } + + return status; +} + +/** + * Checks if specified port is valid and enabled, then disables it + * + * @param port Pointer the port + * + */ +static void check_disable_port(MMAL_PORT_T *port) +{ + if (port && port->is_enabled) + mmal_port_disable(port); +} + +/** + * Handler for sigint signals + * + * @param signal_number ID of incoming signal. + * + */ +static void signal_handler(int signal_number) +{ + if (signal_number == SIGUSR1) + { + // Handle but ignore - prevents us dropping out if started in none-signal mode + // and someone sends us the USR1 signal anyway + } + else + { + // Going to abort on all other signals + vcos_log_error("Aborting program\n"); + exit(130); + } + +} + +/** + * Pause for specified time, but return early if detect an abort request + * + * @param state Pointer to state control struct + * @param pause Time in ms to pause + * @param callback Struct contain an abort flag tested for early termination + * + */ +static int pause_and_test_abort(RASPIVID_STATE *state, int pause) +{ + int wait; + + if (!pause) + return 0; + + // Going to check every ABORT_INTERVAL milliseconds + for (wait = 0; wait < pause; wait+= ABORT_INTERVAL) + { + vcos_sleep(ABORT_INTERVAL); + if (state->callback_data.abort) + return 1; + } + + return 0; +} + + +/** + * Function to wait in various ways (depending on settings) + * + * @param state Pointer to the state data + * + * @return !0 if to continue, 0 if reached end of run + */ +static int wait_for_next_change(RASPIVID_STATE *state) +{ + int keep_running = 1; + static int64_t complete_time = -1; + + // Have we actually exceeded our timeout? + int64_t current_time = vcos_getmicrosecs64()/1000; + + if (complete_time == -1) + complete_time = current_time + state->timeout; + + // if we have run out of time, flag we need to exit + if (current_time >= complete_time && state->timeout != 0) + keep_running = 0; + + switch (state->waitMethod) + { + case WAIT_METHOD_NONE: + (void)pause_and_test_abort(state, state->timeout); + return 0; + + case WAIT_METHOD_FOREVER: + { + // We never return from this. Expect a ctrl-c to exit. + while (1) + // Have a sleep so we don't hog the CPU. + vcos_sleep(10000); + + return 0; + } + + case WAIT_METHOD_TIMED: + { + int abort; + + if (state->bCapturing) + abort = pause_and_test_abort(state, state->onTime); + else + abort = pause_and_test_abort(state, state->offTime); + + if (abort) + return 0; + else + return keep_running; + } + + case WAIT_METHOD_KEYPRESS: + { + char ch; + + if (state->verbose) + fprintf(stderr, "Press Enter to %s, X then ENTER to exit, [i,o,r] then ENTER to change zoom\n", state->bCapturing ? "pause" : "capture"); + + ch = getchar(); + if (ch == 'x' || ch == 'X') + return 0; + else if (ch == 'i' || ch == 'I') + { + if (state->verbose) + fprintf(stderr, "Starting zoom in\n"); + + raspicamcontrol_zoom_in_zoom_out(state->camera_component, ZOOM_IN, &(state->camera_parameters).roi); + + if (state->verbose) + dump_status(state); + } + else if (ch == 'o' || ch == 'O') + { + if (state->verbose) + fprintf(stderr, "Starting zoom out\n"); + + raspicamcontrol_zoom_in_zoom_out(state->camera_component, ZOOM_OUT, &(state->camera_parameters).roi); + + if (state->verbose) + dump_status(state); + } + else if (ch == 'r' || ch == 'R') + { + if (state->verbose) + fprintf(stderr, "starting reset zoom\n"); + + raspicamcontrol_zoom_in_zoom_out(state->camera_component, ZOOM_RESET, &(state->camera_parameters).roi); + + if (state->verbose) + dump_status(state); + } + + return keep_running; + } + + + case WAIT_METHOD_SIGNAL: + { + // Need to wait for a SIGUSR1 signal + sigset_t waitset; + int sig; + int result = 0; + + sigemptyset( &waitset ); + sigaddset( &waitset, SIGUSR1 ); + + // We are multi threaded because we use mmal, so need to use the pthread + // variant of procmask to block SIGUSR1 so we can wait on it. + pthread_sigmask( SIG_BLOCK, &waitset, NULL ); + + if (state->verbose) + { + fprintf(stderr, "Waiting for SIGUSR1 to %s\n", state->bCapturing ? "pause" : "capture"); + } + + result = sigwait( &waitset, &sig ); + + if (state->verbose && result != 0) + fprintf(stderr, "Bad signal received - error %d\n", errno); + + return keep_running; + } + + } // switch + + return keep_running; +} + +/** + * main + */ +int main(int argc, const char **argv) +{ + // Our main data storage vessel.. + RASPIVID_STATE state; + int exit_code = EX_OK; + + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *camera_still_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + MMAL_PORT_T *encoder_input_port = NULL; + MMAL_PORT_T *encoder_output_port = NULL; + MMAL_PORT_T *splitter_input_port = NULL; + MMAL_PORT_T *splitter_output_port = NULL; + MMAL_PORT_T *splitter_preview_port = NULL; + + bcm_host_init(); + + // Register our application with the logging system + vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY); + + signal(SIGINT, signal_handler); + + // Disable USR1 for the moment - may be reenabled if go in to signal capture mode + signal(SIGUSR1, SIG_IGN); + + default_status(&state); + + // Do we have any parameters + if (argc == 1) + { + fprintf(stdout, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + display_valid_parameters(basename(argv[0])); + exit(EX_USAGE); + } + + // Parse the command line and put options in to our status structure + if (parse_cmdline(argc, argv, &state)) + { + status = -1; + exit(EX_USAGE); + } + + if (state.verbose) + { + fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + dump_status(&state); + } + + // OK, we have a nice set of parameters. Now set up our components + // We have three components. Camera, Preview and encoder. + + if ((status = create_camera_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + exit_code = EX_SOFTWARE; + } + else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create encode component", __func__); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else if (state.raw_output && (status = create_splitter_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create splitter component", __func__); + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + destroy_encoder_component(&state); + exit_code = EX_SOFTWARE; + } + else + { + if (state.verbose) + fprintf(stderr, "Starting component connection stage\n"); + + camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + preview_input_port = state.preview_parameters.preview_component->input[0]; + encoder_input_port = state.encoder_component->input[0]; + encoder_output_port = state.encoder_component->output[0]; + + if (state.raw_output) + { + splitter_input_port = state.splitter_component->input[0]; + splitter_output_port = state.splitter_component->output[SPLITTER_OUTPUT_PORT]; + splitter_preview_port = state.splitter_component->output[SPLITTER_PREVIEW_PORT]; + } + + if (state.preview_parameters.wantPreview ) + { + if (state.raw_output) + { + if (state.verbose) + fprintf(stderr, "Connecting camera preview port to splitter input port\n"); + + // Connect camera to splitter + status = connect_ports(camera_preview_port, splitter_input_port, &state.splitter_connection); + + if (status != MMAL_SUCCESS) + { + state.splitter_connection = NULL; + vcos_log_error("%s: Failed to connect camera preview port to splitter input", __func__); + goto error; + } + + if (state.verbose) + { + fprintf(stderr, "Connecting splitter preview port to preview input port\n"); + fprintf(stderr, "Starting video preview\n"); + } + + // Connect splitter to preview + status = connect_ports(splitter_preview_port, preview_input_port, &state.preview_connection); + } + else + { + if (state.verbose) + { + fprintf(stderr, "Connecting camera preview port to preview input port\n"); + fprintf(stderr, "Starting video preview\n"); + } + + // Connect camera to preview + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + } + + if (status != MMAL_SUCCESS) + state.preview_connection = NULL; + } + else + { + if (state.raw_output) + { + if (state.verbose) + fprintf(stderr, "Connecting camera preview port to splitter input port\n"); + + // Connect camera to splitter + status = connect_ports(camera_preview_port, splitter_input_port, &state.splitter_connection); + + if (status != MMAL_SUCCESS) + { + state.splitter_connection = NULL; + vcos_log_error("%s: Failed to connect camera preview port to splitter input", __func__); + goto error; + } + } + else + { + status = MMAL_SUCCESS; + } + } + + if (status == MMAL_SUCCESS) + { + if (state.verbose) + fprintf(stderr, "Connecting camera video port to encoder input port\n"); + + // Now connect the camera to the encoder + status = connect_ports(camera_video_port, encoder_input_port, &state.encoder_connection); + + if (status != MMAL_SUCCESS) + { + state.encoder_connection = NULL; + vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__); + goto error; + } + } + + if (status == MMAL_SUCCESS) + { + // Set up our userdata - this is passed though to the callback where we need the information. + state.callback_data.pstate = &state; + state.callback_data.abort = 0; + + if (state.raw_output) + { + splitter_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state.callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling splitter output port\n"); + + // Enable the splitter output port and tell it its callback function + status = mmal_port_enable(splitter_output_port, splitter_buffer_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to setup splitter output port", __func__); + goto error; + } + } + + state.callback_data.file_handle = NULL; + + if (state.filename) + { + if (state.filename[0] == '-') + { + state.callback_data.file_handle = stdout; + + // Ensure we don't upset the output stream with diagnostics/info + state.verbose = 0; + } + else + { + state.callback_data.file_handle = open_filename(&state, state.filename); + } + + if (!state.callback_data.file_handle) + { + // Notify user, carry on but discarding encoded output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.filename); + } + } + + state.callback_data.imv_file_handle = NULL; + + if (state.imv_filename) + { + if (state.imv_filename[0] == '-') + { + state.callback_data.imv_file_handle = stdout; + } + else + { + state.callback_data.imv_file_handle = open_filename(&state, state.imv_filename); + } + + if (!state.callback_data.imv_file_handle) + { + // Notify user, carry on but discarding encoded output buffers + fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n",state.imv_filename); + state.inlineMotionVectors=0; + } + } + + state.callback_data.pts_file_handle = NULL; + + if (state.pts_filename) + { + if (state.pts_filename[0] == '-') + { + state.callback_data.pts_file_handle = stdout; + } + else + { + state.callback_data.pts_file_handle = open_filename(&state, state.pts_filename); + if (state.callback_data.pts_file_handle) /* save header for mkvmerge */ + fprintf(state.callback_data.pts_file_handle, "# timecode format v2\n"); + } + + if (!state.callback_data.pts_file_handle) + { + // Notify user, carry on but discarding encoded output buffers + fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n",state.pts_filename); + state.save_pts=0; + } + } + + state.callback_data.raw_file_handle = NULL; + + if (state.raw_filename) + { + if (state.raw_filename[0] == '-') + { + state.callback_data.raw_file_handle = stdout; + } + else + { + state.callback_data.raw_file_handle = open_filename(&state, state.raw_filename); + } + + if (!state.callback_data.raw_file_handle) + { + // Notify user, carry on but discarding encoded output buffers + fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n", state.raw_filename); + state.raw_output = 0; + } + } + + if(state.bCircularBuffer) + { + if(state.bitrate == 0) + { + vcos_log_error("%s: Error circular buffer requires constant bitrate and small intra period\n", __func__); + goto error; + } + else if(state.timeout == 0) + { + vcos_log_error("%s: Error, circular buffer size is based on timeout must be greater than zero\n", __func__); + goto error; + } + else if(state.waitMethod != WAIT_METHOD_KEYPRESS && state.waitMethod != WAIT_METHOD_SIGNAL) + { + vcos_log_error("%s: Error, Circular buffer mode requires either keypress (-k) or signal (-s) triggering\n", __func__); + goto error; + } + else if(!state.callback_data.file_handle) + { + vcos_log_error("%s: Error require output file (or stdout) for Circular buffer mode\n", __func__); + goto error; + } + else + { + int count = state.bitrate * (state.timeout / 1000) / 8; + + state.callback_data.cb_buff = (char *) malloc(count); + if(state.callback_data.cb_buff == NULL) + { + vcos_log_error("%s: Unable to allocate circular buffer for %d seconds at %.1f Mbits\n", __func__, state.timeout / 1000, (double)state.bitrate/1000000.0); + goto error; + } + else + { + state.callback_data.cb_len = count; + state.callback_data.cb_wptr = 0; + state.callback_data.cb_wrap = 0; + state.callback_data.cb_data = 0; + state.callback_data.iframe_buff_wpos = 0; + state.callback_data.iframe_buff_rpos = 0; + state.callback_data.header_wptr = 0; + } + } + } + + // Set up our userdata - this is passed though to the callback where we need the information. + encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state.callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling encoder output port\n"); + + // Enable the encoder output port and tell it its callback function + status = mmal_port_enable(encoder_output_port, encoder_buffer_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup encoder output"); + goto error; + } + + if (state.demoMode) + { + // Run for the user specific time.. + int num_iterations = state.timeout / state.demoInterval; + int i; + + if (state.verbose) + fprintf(stderr, "Running in demo mode\n"); + + for (i=0;state.timeout == 0 || iqueue); + int q; + for (q=0;qqueue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(encoder_output_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to encoder output port (%d)", q); + } + } + + // Send all the buffers to the splitter output port + if (state.raw_output) { + int num = mmal_queue_length(state.splitter_pool->queue); + int q; + for (q = 0; q < num; q++) + { + MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.splitter_pool->queue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(splitter_output_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to splitter output port (%d)", q); + } + } + + int initialCapturing=state.bCapturing; + while (running) + { + // Change state + + state.bCapturing = !state.bCapturing; + + if (mmal_port_parameter_set_boolean(camera_video_port, MMAL_PARAMETER_CAPTURE, state.bCapturing) != MMAL_SUCCESS) + { + // How to handle? + } + + // In circular buffer mode, exit and save the buffer (make sure we do this after having paused the capture + if(state.bCircularBuffer && !state.bCapturing) + { + break; + } + + if (state.verbose) + { + if (state.bCapturing) + fprintf(stderr, "Starting video capture\n"); + else + fprintf(stderr, "Pausing video capture\n"); + } + + if(state.splitWait) + { + if(state.bCapturing) + { + if (mmal_port_parameter_set_boolean(encoder_output_port, MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, 1) != MMAL_SUCCESS) + { + vcos_log_error("failed to request I-FRAME"); + } + } + else + { + if(!initialCapturing) + state.splitNow=1; + } + initialCapturing=0; + } + running = wait_for_next_change(&state); + } + + if (state.verbose) + fprintf(stderr, "Finished capture\n"); + } + else + { + if (state.timeout) + vcos_sleep(state.timeout); + else + { + // timeout = 0 so run forever + while(1) + vcos_sleep(ABORT_INTERVAL); + } + } + } + } + else + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + } + + if(state.bCircularBuffer) + { + int copy_from_end, copy_from_start; + + copy_from_end = state.callback_data.cb_len - state.callback_data.iframe_buff[state.callback_data.iframe_buff_rpos]; + copy_from_start = state.callback_data.cb_len - copy_from_end; + copy_from_start = state.callback_data.cb_wptr < copy_from_start ? state.callback_data.cb_wptr : copy_from_start; + if(!state.callback_data.cb_wrap) + { + copy_from_start = state.callback_data.cb_wptr; + copy_from_end = 0; + } + + fwrite(state.callback_data.header_bytes, 1, state.callback_data.header_wptr, state.callback_data.file_handle); + // Save circular buffer + fwrite(state.callback_data.cb_buff + state.callback_data.iframe_buff[state.callback_data.iframe_buff_rpos], 1, copy_from_end, state.callback_data.file_handle); + fwrite(state.callback_data.cb_buff, 1, copy_from_start, state.callback_data.file_handle); + if(state.callback_data.flush_buffers) fflush(state.callback_data.file_handle); + } + +error: + + mmal_status_to_int(status); + + if (state.verbose) + fprintf(stderr, "Closing down\n"); + + // Disable all our ports that are not handled by connections + check_disable_port(camera_still_port); + check_disable_port(encoder_output_port); + check_disable_port(splitter_output_port); + + if (state.preview_parameters.wantPreview && state.preview_connection) + mmal_connection_destroy(state.preview_connection); + + if (state.encoder_connection) + mmal_connection_destroy(state.encoder_connection); + + if (state.splitter_connection) + mmal_connection_destroy(state.splitter_connection); + + // Can now close our file. Note disabling ports may flush buffers which causes + // problems if we have already closed the file! + if (state.callback_data.file_handle && state.callback_data.file_handle != stdout) + fclose(state.callback_data.file_handle); + if (state.callback_data.imv_file_handle && state.callback_data.imv_file_handle != stdout) + fclose(state.callback_data.imv_file_handle); + if (state.callback_data.pts_file_handle && state.callback_data.pts_file_handle != stdout) + fclose(state.callback_data.pts_file_handle); + if (state.callback_data.raw_file_handle && state.callback_data.raw_file_handle != stdout) + fclose(state.callback_data.raw_file_handle); + + /* Disable components */ + if (state.encoder_component) + mmal_component_disable(state.encoder_component); + + if (state.preview_parameters.preview_component) + mmal_component_disable(state.preview_parameters.preview_component); + + if (state.splitter_component) + mmal_component_disable(state.splitter_component); + + if (state.camera_component) + mmal_component_disable(state.camera_component); + + destroy_encoder_component(&state); + raspipreview_destroy(&state.preview_parameters); + destroy_splitter_component(&state); + destroy_camera_component(&state); + + if (state.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + } + + if (status != MMAL_SUCCESS) + raspicamcontrol_check_configuration(128); + + return exit_code; +} diff --git a/host_applications/linux/apps/raspicam/RaspiVidYUV.c b/host_applications/linux/apps/raspicam/RaspiVidYUV.c new file mode 100755 index 0000000..7034b40 --- /dev/null +++ b/host_applications/linux/apps/raspicam/RaspiVidYUV.c @@ -0,0 +1,1651 @@ +/* +Copyright (c) 2014, DSP Group Ltd +Copyright (c) 2014, James Hughes +Copyright (c) 2013, Broadcom Europe Ltd + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + * \file RaspiVidYUV.c + * Command line program to capture a camera video stream and save file + * as uncompressed YUV420 data + * Also optionally display a preview/viewfinder of current camera input. + * + * \date 7th Jan 2014 + * \Author: James Hughes + * + * Description + * + * 2 components are created; camera and preview. + * Camera component has three ports, preview, video and stills. + * Preview is connected using standard mmal connections, the video output + * is written straight to the file in YUV 420 format via the requisite buffer + * callback. Still port is not used + * + * We use the RaspiCamControl code to handle the specific camera settings. + * We use the RaspiPreview code to handle the generic preview +*/ + +// We use some GNU extensions (basename) +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define VERSION_STRING "v1.3.15" + +#include "bcm_host.h" +#include "interface/vcos/vcos.h" + +#include "interface/mmal/mmal.h" +#include "interface/mmal/mmal_logging.h" +#include "interface/mmal/mmal_buffer.h" +#include "interface/mmal/util/mmal_util.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/util/mmal_default_components.h" +#include "interface/mmal/util/mmal_connection.h" + +#include "RaspiCamControl.h" +#include "RaspiPreview.h" +#include "RaspiCLI.h" + +#include + +// Standard port setting for the camera component +#define MMAL_CAMERA_PREVIEW_PORT 0 +#define MMAL_CAMERA_VIDEO_PORT 1 +#define MMAL_CAMERA_CAPTURE_PORT 2 + +// Video format information +// 0 implies variable +#define VIDEO_FRAME_RATE_NUM 30 +#define VIDEO_FRAME_RATE_DEN 1 + +/// Video render needs at least 2 buffers. +#define VIDEO_OUTPUT_BUFFERS_NUM 3 + +/// Interval at which we check for an failure abort during capture +const int ABORT_INTERVAL = 100; // ms + + +/// Capture/Pause switch method +/// Simply capture for time specified +#define WAIT_METHOD_NONE 0 +/// Cycle between capture and pause for times specified +#define WAIT_METHOD_TIMED 1 +/// Switch between capture and pause on keypress +#define WAIT_METHOD_KEYPRESS 2 +/// Switch between capture and pause on signal +#define WAIT_METHOD_SIGNAL 3 +/// Run/record forever +#define WAIT_METHOD_FOREVER 4 + +int mmal_status_to_int(MMAL_STATUS_T status); +static void signal_handler(int signal_number); + +// Forward +typedef struct RASPIVIDYUV_STATE_S RASPIVIDYUV_STATE; + +/** Struct used to pass information in camera video port userdata to callback + */ +typedef struct +{ + FILE *file_handle; /// File handle to write buffer data to. + RASPIVIDYUV_STATE *pstate; /// pointer to our state in case required in callback + int abort; /// Set to 1 in callback if an error occurs to attempt to abort the capture + FILE *pts_file_handle; /// File timestamps +} PORT_USERDATA; + +/** Structure containing all state information for the current run + */ +struct RASPIVIDYUV_STATE_S +{ + int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds + int width; /// Requested width of image + int height; /// requested height of image + int framerate; /// Requested frame rate (fps) + char *filename; /// filename of output file + int verbose; /// !0 if want detailed run information + int demoMode; /// Run app in demo mode + int demoInterval; /// Interval between camera settings changes + int waitMethod; /// Method for switching between pause and capture + + int onTime; /// In timed cycle mode, the amount of time the capture is on per cycle + int offTime; /// In timed cycle mode, the amount of time the capture is off per cycle + + int onlyLuma; /// Only output the luma / Y plane of the YUV data + int useRGB; /// Output RGB data rather than YUV + + RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters + RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters + + MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component + MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview + + MMAL_POOL_T *camera_pool; /// Pointer to the pool of buffers used by camera video port + + PORT_USERDATA callback_data; /// Used to move data to the camera callback + + int bCapturing; /// State of capture/pause + + int cameraNum; /// Camera number + int settings; /// Request settings from the camera + int sensor_mode; /// Sensor mode. 0=auto. Check docs/forum for modes selected by other values. + + int frame; + char *pts_filename; + int save_pts; + int64_t starttime; + int64_t lasttime; + + bool netListen; +}; + +static XREF_T initial_map[] = +{ + {"record", 0}, + {"pause", 1}, +}; + +static int initial_map_size = sizeof(initial_map) / sizeof(initial_map[0]); + + +static void display_valid_parameters(char *app_name); + +/// Command ID's and Structure defining our command line options +#define CommandHelp 0 +#define CommandWidth 1 +#define CommandHeight 2 +#define CommandOutput 3 +#define CommandVerbose 4 +#define CommandTimeout 5 +#define CommandDemoMode 6 +#define CommandFramerate 7 +#define CommandTimed 8 +#define CommandSignal 9 +#define CommandKeypress 10 +#define CommandInitialState 11 +#define CommandCamSelect 12 +#define CommandSettings 13 +#define CommandSensorMode 14 +#define CommandOnlyLuma 15 +#define CommandUseRGB 16 +#define CommandSavePTS 17 +#define CommandNetListen 18 + +static COMMAND_LIST cmdline_commands[] = +{ + { CommandHelp, "-help", "?", "This help information", 0 }, + { CommandWidth, "-width", "w", "Set image width . Default 1920", 1 }, + { CommandHeight, "-height", "h", "Set image height . Default 1080", 1 }, + { CommandOutput, "-output", "o", "Output filename (to write to stdout, use '-o -')", 1 }, + { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 }, + { CommandTimeout, "-timeout", "t", "Time (in ms) to capture for. If not specified, set to 5s. Zero to disable", 1 }, + { CommandDemoMode, "-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 1}, + { CommandFramerate, "-framerate", "fps","Specify the frames per second to record", 1}, + { CommandTimed, "-timed", "td", "Cycle between capture and pause. -cycle on,off where on is record time and off is pause time in ms", 0}, + { CommandSignal, "-signal", "s", "Cycle between capture and pause on Signal", 0}, + { CommandKeypress, "-keypress", "k", "Cycle between capture and pause on ENTER", 0}, + { CommandInitialState, "-initial", "i", "Initial state. Use 'record' or 'pause'. Default 'record'", 1}, + { CommandCamSelect, "-camselect", "cs", "Select camera . Default 0", 1 }, + { CommandSettings, "-settings", "set","Retrieve camera settings and write to stdout", 0}, + { CommandSensorMode, "-mode", "md", "Force sensor mode. 0=auto. See docs for other modes available", 1}, + { CommandOnlyLuma, "-luma", "y", "Only output the luma / Y of the YUV data'", 0}, + { CommandUseRGB, "-rgb", "rgb","Save as RGB data rather than YUV", 0}, + { CommandSavePTS, "-save-pts", "pts","Save Timestamps to file", 1 }, + { CommandNetListen, "-listen", "l", "Listen on a TCP socket", 0}, +}; + +static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); + + +static struct +{ + char *description; + int nextWaitMethod; +} wait_method_description[] = +{ + {"Simple capture", WAIT_METHOD_NONE}, + {"Capture forever", WAIT_METHOD_FOREVER}, + {"Cycle on time", WAIT_METHOD_TIMED}, + {"Cycle on keypress", WAIT_METHOD_KEYPRESS}, + {"Cycle on signal", WAIT_METHOD_SIGNAL}, +}; + +static int wait_method_description_size = sizeof(wait_method_description) / sizeof(wait_method_description[0]); + + + +/** + * Assign a default set of parameters to the state passed in + * + * @param state Pointer to state structure to assign defaults to + */ +static void default_status(RASPIVIDYUV_STATE *state) +{ + if (!state) + { + vcos_assert(0); + return; + } + + // Default everything to zero + memset(state, 0, sizeof(RASPIVIDYUV_STATE)); + + // Now set anything non-zero + state->timeout = 5000; // 5s delay before take image + state->width = 1920; // Default to 1080p + state->height = 1080; + state->framerate = VIDEO_FRAME_RATE_NUM; + state->demoMode = 0; + state->demoInterval = 250; // ms + state->waitMethod = WAIT_METHOD_NONE; + state->onTime = 5000; + state->offTime = 5000; + + state->bCapturing = 0; + + state->cameraNum = 0; + state->settings = 0; + state->sensor_mode = 0; + state->onlyLuma = 0; + + // Setup preview window defaults + raspipreview_set_defaults(&state->preview_parameters); + + // Set up the camera_parameters to default + raspicamcontrol_set_defaults(&state->camera_parameters); +} + + +/** + * Dump image state parameters to stderr. + * + * @param state Pointer to state structure to assign defaults to + */ +static void dump_status(RASPIVIDYUV_STATE *state) +{ + int i, size, ystride, yheight; + + if (!state) + { + vcos_assert(0); + return; + } + + fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, state->height, state->filename); + fprintf(stderr, "framerate %d, time delay %d\n", state->framerate, state->timeout); + + // Calculate the individual image size + // Y stride rounded to multiple of 32. U&V stride is Y stride/2 (ie multiple of 16). + // Y height is padded to a 16. U/V height is Y height/2 (ie multiple of 8). + + // Y plane + ystride = ((state->width + 31) & ~31); + yheight = ((state->height + 15) & ~15); + + size = ystride * yheight; + + // U and V plane + size += 2 * ystride/2 * yheight/2; + + fprintf(stderr, "Sub-image size %d bytes in total.\n Y pitch %d, Y height %d, UV pitch %d, UV Height %d\n", size, ystride, yheight, ystride/2,yheight/2); + + fprintf(stderr, "Wait method : "); + for (i=0;iwaitMethod == wait_method_description[i].nextWaitMethod) + fprintf(stderr, "%s", wait_method_description[i].description); + } + fprintf(stderr, "\nInitial state '%s'\n", raspicli_unmap_xref(state->bCapturing, initial_map, initial_map_size)); + fprintf(stderr, "\n\n"); + + raspipreview_dump_parameters(&state->preview_parameters); + raspicamcontrol_dump_parameters(&state->camera_parameters); +} + +/** + * Parse the incoming command line and put resulting parameters in to the state + * + * @param argc Number of arguments in command line + * @param argv Array of pointers to strings from command line + * @param state Pointer to state structure to assign any discovered parameters to + * @return Non-0 if failed for some reason, 0 otherwise + */ +static int parse_cmdline(int argc, const char **argv, RASPIVIDYUV_STATE *state) +{ + // Parse the command line arguments. + // We are looking for -- or - + + int valid = 1; + int i; + + for (i = 1; i < argc && valid; i++) + { + int command_id, num_parameters; + + if (!argv[i]) + continue; + + if (argv[i][0] != '-') + { + valid = 0; + continue; + } + + // Assume parameter is valid until proven otherwise + valid = 1; + + command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters); + + // If we found a command but are missing a parameter, continue (and we will drop out of the loop) + if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) ) + continue; + + // We are now dealing with a command line option + switch (command_id) + { + case CommandHelp: + display_valid_parameters(basename(argv[0])); + return -1; + + case CommandWidth: // Width > 0 + if (sscanf(argv[i + 1], "%u", &state->width) != 1) + valid = 0; + else + i++; + break; + + case CommandHeight: // Height > 0 + if (sscanf(argv[i + 1], "%u", &state->height) != 1) + valid = 0; + else + i++; + break; + + case CommandOutput: // output filename + { + int len = strlen(argv[i + 1]); + if (len) + { + state->filename = malloc(len + 1); + vcos_assert(state->filename); + if (state->filename) + strncpy(state->filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandVerbose: // display lots of data during run + state->verbose = 1; + break; + + case CommandTimeout: // Time to run viewfinder/capture + { + if (sscanf(argv[i + 1], "%u", &state->timeout) == 1) + { + // Ensure that if previously selected a waitMethod we don't overwrite it + if (state->timeout == 0 && state->waitMethod == WAIT_METHOD_NONE) + state->waitMethod = WAIT_METHOD_FOREVER; + + i++; + } + else + valid = 0; + break; + } + + case CommandDemoMode: // Run in demo mode - no capture + { + // Demo mode might have a timing parameter + // so check if a) we have another parameter, b) its not the start of the next option + if (i + 1 < argc && argv[i+1][0] != '-') + { + if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1) + { + // TODO : What limits do we need for timeout? + if (state->demoInterval == 0) + state->demoInterval = 250; // ms + + state->demoMode = 1; + i++; + } + else + valid = 0; + } + else + { + state->demoMode = 1; + } + + break; + } + + case CommandFramerate: // fps to record + { + if (sscanf(argv[i + 1], "%u", &state->framerate) == 1) + { + // TODO : What limits do we need for fps 1 - 30 - 120?? + i++; + } + else + valid = 0; + break; + } + + case CommandTimed: + { + if (sscanf(argv[i + 1], "%u,%u", &state->onTime, &state->offTime) == 2) + { + i++; + + if (state->onTime < 1000) + state->onTime = 1000; + + if (state->offTime < 1000) + state->offTime = 1000; + + state->waitMethod = WAIT_METHOD_TIMED; + } + else + valid = 0; + break; + } + + case CommandKeypress: + state->waitMethod = WAIT_METHOD_KEYPRESS; + break; + + case CommandSignal: + state->waitMethod = WAIT_METHOD_SIGNAL; + // Reenable the signal + signal(SIGUSR1, signal_handler); + break; + + case CommandInitialState: + { + state->bCapturing = raspicli_map_xref(argv[i + 1], initial_map, initial_map_size); + + if( state->bCapturing == -1) + state->bCapturing = 0; + + i++; + break; + } + + case CommandCamSelect: //Select camera input port + { + if (sscanf(argv[i + 1], "%u", &state->cameraNum) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandSettings: + state->settings = 1; + break; + + case CommandSensorMode: + { + if (sscanf(argv[i + 1], "%u", &state->sensor_mode) == 1) + { + i++; + } + else + valid = 0; + break; + } + + case CommandOnlyLuma: + if (state->useRGB) + { + fprintf(stderr, "--luma and --rgb are mutually exclusive\n"); + valid = 0; + } + state->onlyLuma = 1; + break; + + case CommandUseRGB: // display lots of data during run + if (state->onlyLuma) + { + fprintf(stderr, "--luma and --rgb are mutually exclusive\n"); + valid = 0; + } + state->useRGB = 1; + break; + + case CommandSavePTS: // output filename + { + state->save_pts = 1; + int len = strlen(argv[i + 1]); + if (len) + { + state->pts_filename = malloc(len + 1); + vcos_assert(state->pts_filename); + if (state->pts_filename) + strncpy(state->pts_filename, argv[i + 1], len+1); + i++; + } + else + valid = 0; + break; + } + + case CommandNetListen: + { + state->netListen = true; + + break; + } + + default: + { + // Try parsing for any image specific parameters + // result indicates how many parameters were used up, 0,1,2 + // but we adjust by -1 as we have used one already + const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL; + int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg)); + + // Still unused, try preview options + if (!parms_used) + parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg); + + + // If no parms were used, this must be a bad parameters + if (!parms_used) + valid = 0; + else + i += parms_used - 1; + + break; + } + } + } + + if (!valid) + { + fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]); + return 1; + } + + // Always disable verbose if output going to stdout + if (state->filename && state->filename[0] == '-') + { + state->verbose = 0; + } + + return 0; +} + +/** + * Display usage information for the application to stdout + * + * @param app_name String to display as the application name + */ +static void display_valid_parameters(char *app_name) +{ + fprintf(stdout, "Display camera output to display, and optionally saves an uncompressed YUV420 file \n\n"); + fprintf(stdout, "NOTE: High resolutions and/or frame rates may exceed the bandwidth of the system due\n"); + fprintf(stdout, "to the large amounts of data being moved to the SD card. This will result in undefined\n"); + fprintf(stdout, "results in the subsequent file.\n"); + fprintf(stdout, "The raw file produced contains all the files. Each image in the files will be of size\n"); + fprintf(stdout, "width*height*1.5, unless width and/or height are not divisible by 16. Use the image size\n"); + fprintf(stdout, "displayed during the run (in verbose mode) for an accurate value\n"); + + fprintf(stdout, "The Linux split command can be used to split up the file to individual frames\n"); + + fprintf(stdout, "\nusage: %s [options]\n\n", app_name); + + fprintf(stdout, "Image parameter commands\n\n"); + + raspicli_display_help(cmdline_commands, cmdline_commands_size); + + fprintf(stdout, "\n"); + + // Help for preview options + raspipreview_display_help(); + + // Now display any help information from the camcontrol code + raspicamcontrol_display_help(); + + fprintf(stdout, "\n"); + + return; +} + +/** + * buffer header callback function for camera control + * + * Callback will dump buffer data to the specific file + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED) + { + MMAL_EVENT_PARAMETER_CHANGED_T *param = (MMAL_EVENT_PARAMETER_CHANGED_T *)buffer->data; + switch (param->hdr.id) { + case MMAL_PARAMETER_CAMERA_SETTINGS: + { + MMAL_PARAMETER_CAMERA_SETTINGS_T *settings = (MMAL_PARAMETER_CAMERA_SETTINGS_T*)param; + vcos_log_error("Exposure now %u, analog gain %u/%u, digital gain %u/%u", + settings->exposure, + settings->analog_gain.num, settings->analog_gain.den, + settings->digital_gain.num, settings->digital_gain.den); + vcos_log_error("AWB R=%u/%u, B=%u/%u", + settings->awb_red_gain.num, settings->awb_red_gain.den, + settings->awb_blue_gain.num, settings->awb_blue_gain.den + ); + } + break; + } + } + else if (buffer->cmd == MMAL_EVENT_ERROR) + { + vcos_log_error("No data received from sensor. Check all connections, including the Sunny one on the camera board"); + } + else + { + vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd); + } + + mmal_buffer_header_release(buffer); +} + + +/** + * Open a file based on the settings in state + * + * @param state Pointer to state + */ +static FILE *open_filename(RASPIVIDYUV_STATE *pState, char *filename) +{ + FILE *new_handle = NULL; + + if (filename) + { + bool bNetwork = false; + int sfd = -1, socktype; + + if(!strncmp("tcp://", filename, 6)) + { + bNetwork = true; + socktype = SOCK_STREAM; + } + else if(!strncmp("udp://", filename, 6)) + { + if (pState->netListen) + { + fprintf(stderr, "No support for listening in UDP mode\n"); + exit(131); + } + bNetwork = true; + socktype = SOCK_DGRAM; + } + + if(bNetwork) + { + unsigned short port; + filename += 6; + char *colon; + if(NULL == (colon = strchr(filename, ':'))) + { + fprintf(stderr, "%s is not a valid IPv4:port, use something like tcp://1.2.3.4:1234 or udp://1.2.3.4:1234\n", + filename); + exit(132); + } + if(1 != sscanf(colon + 1, "%hu", &port)) + { + fprintf(stderr, + "Port parse failed. %s is not a valid network file name, use something like tcp://1.2.3.4:1234 or udp://1.2.3.4:1234\n", + filename); + exit(133); + } + char chTmp = *colon; + *colon = 0; + + struct sockaddr_in saddr={}; + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + if(0 == inet_aton(filename, &saddr.sin_addr)) + { + fprintf(stderr, "inet_aton failed. %s is not a valid IPv4 address\n", + filename); + exit(134); + } + *colon = chTmp; + + if (pState->netListen) + { + int sockListen = socket(AF_INET, SOCK_STREAM, 0); + if (sockListen >= 0) + { + int iTmp = 1; + setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &iTmp, sizeof(int));//no error handling, just go on + if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0) + { + while ((-1 == (iTmp = listen(sockListen, 0))) && (EINTR == errno)) + ; + if (-1 != iTmp) + { + fprintf(stderr, "Waiting for a TCP connection on %s:%"SCNu16"...", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + struct sockaddr_in cli_addr; + socklen_t clilen = sizeof(cli_addr); + while ((-1 == (sfd = accept(sockListen, (struct sockaddr *) &cli_addr, &clilen))) && (EINTR == errno)) + ; + if (sfd >= 0) + fprintf(stderr, "Client connected from %s:%"SCNu16"\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); + else + fprintf(stderr, "Error on accept: %s\n", strerror(errno)); + } + else//if (-1 != iTmp) + { + fprintf(stderr, "Error trying to listen on a socket: %s\n", strerror(errno)); + } + } + else//if (bind(sockListen, (struct sockaddr *) &saddr, sizeof(saddr)) >= 0) + { + fprintf(stderr, "Error on binding socket: %s\n", strerror(errno)); + } + } + else//if (sockListen >= 0) + { + fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); + } + + if (sockListen >= 0)//regardless success or error + close(sockListen);//do not listen on a given port anymore + } + else//if (pState->netListen) + { + if(0 <= (sfd = socket(AF_INET, socktype, 0))) + { + fprintf(stderr, "Connecting to %s:%hu...", inet_ntoa(saddr.sin_addr), port); + + int iTmp = 1; + while ((-1 == (iTmp = connect(sfd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in)))) && (EINTR == errno)) + ; + if (iTmp < 0) + fprintf(stderr, "error: %s\n", strerror(errno)); + else + fprintf(stderr, "connected, sending video...\n"); + } + else + fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); + } + + if (sfd >= 0) + new_handle = fdopen(sfd, "w"); + } + else + { + new_handle = fopen(filename, "wb"); + } + } + + if (pState->verbose) + { + if (new_handle) + fprintf(stderr, "Opening output file \"%s\"\n", filename); + else + fprintf(stderr, "Failed to open new file \"%s\"\n", filename); + } + + return new_handle; +} + +/** + * buffer header callback function for camera + * + * Callback will dump buffer data to internal buffer + * + * @param port Pointer to port from which callback originated + * @param buffer mmal buffer header pointer + */ +static void camera_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +{ + MMAL_BUFFER_HEADER_T *new_buffer; + static int64_t base_time = -1; + + // All our times based on the receipt of the first callback + if (base_time == -1) + base_time = vcos_getmicrosecs64()/1000; + + // We pass our file handle and other stuff in via the userdata field. + + PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; + + if (pData) + { + int bytes_written = 0; + int bytes_to_write = buffer->length; + + if (pData->pstate->onlyLuma) + bytes_to_write = vcos_min(buffer->length, port->format->es->video.width * port->format->es->video.height); + + vcos_assert(pData->file_handle); + + if (bytes_to_write) + { + mmal_buffer_header_mem_lock(buffer); + bytes_written = fwrite(buffer->data, 1, bytes_to_write, pData->file_handle); + mmal_buffer_header_mem_unlock(buffer); + + if (bytes_written != bytes_to_write) + { + vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, bytes_to_write); + pData->abort = 1; + } + } + } + else + { + vcos_log_error("Received a camera buffer callback with no state"); + } + + // release buffer back to the pool + mmal_buffer_header_release(buffer); + + // and send one back to the port (if still open) + if (port->is_enabled) + { + MMAL_STATUS_T status; + + new_buffer = mmal_queue_get(pData->pstate->camera_pool->queue); + + if (new_buffer) + status = mmal_port_send_buffer(port, new_buffer); + + if (!new_buffer || status != MMAL_SUCCESS) + vcos_log_error("Unable to return a buffer to the camera port"); + } +} + + +/** + * Create the camera component, set up its ports + * + * @param state Pointer to state control struct + * + * @return MMAL_SUCCESS if all OK, something else otherwise + * + */ +static MMAL_STATUS_T create_camera_component(RASPIVIDYUV_STATE *state) +{ + MMAL_COMPONENT_T *camera = 0; + MMAL_ES_FORMAT_T *format; + MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL; + MMAL_STATUS_T status; + MMAL_POOL_T *pool; + + /* Create the component */ + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to create camera component"); + goto error; + } + + MMAL_PARAMETER_INT32_T camera_num = + {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, state->cameraNum}; + + status = mmal_port_parameter_set(camera->control, &camera_num.hdr); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not select camera : error %d", status); + goto error; + } + + if (!camera->output_num) + { + status = MMAL_ENOSYS; + vcos_log_error("Camera doesn't have output ports"); + goto error; + } + + status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, state->sensor_mode); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Could not set sensor mode : error %d", status); + goto error; + } + + preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT]; + video_port = camera->output[MMAL_CAMERA_VIDEO_PORT]; + still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT]; + + if (state->settings) + { + MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T change_event_request = + {{MMAL_PARAMETER_CHANGE_EVENT_REQUEST, sizeof(MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T)}, + MMAL_PARAMETER_CAMERA_SETTINGS, 1}; + + status = mmal_port_parameter_set(camera->control, &change_event_request.hdr); + if ( status != MMAL_SUCCESS ) + { + vcos_log_error("No camera settings events"); + } + } + + // Enable the camera, and tell it its control callback function + status = mmal_port_enable(camera->control, camera_control_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Unable to enable control port : error %d", status); + goto error; + } + + // set up the camera configuration + { + MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = + { + { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, + .max_stills_w = state->width, + .max_stills_h = state->height, + .stills_yuv422 = 0, + .one_shot_stills = 0, + .max_preview_video_w = state->width, + .max_preview_video_h = state->height, + .num_preview_video_frames = 3, + .stills_capture_circular_buffer_height = 0, + .fast_preview_resume = 0, + .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC + }; + mmal_port_parameter_set(camera->control, &cam_config.hdr); + } + + // Now set up the port formats + + // Set the encode format on the Preview port + // HW limitations mean we need the preview to be the same size as the required recorded output + + format = preview_port->format; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 166, 1000 }, {999, 1000}}; + mmal_port_parameter_set(preview_port, &fps_range.hdr); + } + + //enable dynamic framerate if necessary + if (state->camera_parameters.shutter_speed) + { + if (state->framerate > 1000000./state->camera_parameters.shutter_speed) + { + state->framerate=0; + if (state->verbose) + fprintf(stderr, "Enable dynamic frame rate to fulfil shutter speed requirement\n"); + } + } + + format->encoding = MMAL_ENCODING_OPAQUE; + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; + format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; + + status = mmal_port_format_commit(preview_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera viewfinder format couldn't be set"); + goto error; + } + + // Set the encode format on the video port + + format = video_port->format; + + if(state->camera_parameters.shutter_speed > 6000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 50, 1000 }, {166, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + else if(state->camera_parameters.shutter_speed > 1000000) + { + MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)}, + { 167, 1000 }, {999, 1000}}; + mmal_port_parameter_set(video_port, &fps_range.hdr); + } + + if (state->useRGB) + { + format->encoding = mmal_util_rgb_order_fixed(still_port) ? MMAL_ENCODING_RGB24 : MMAL_ENCODING_BGR24; + format->encoding_variant = 0; //Irrelevant when not in opaque mode + } + else + { + format->encoding = MMAL_ENCODING_I420; + format->encoding_variant = MMAL_ENCODING_I420; + } + + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = state->framerate; + format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; + + status = mmal_port_format_commit(video_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera video format couldn't be set"); + goto error; + } + + // Ensure there are enough buffers to avoid dropping frames + if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + status = mmal_port_parameter_set_boolean(video_port, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE); + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to select zero copy"); + goto error; + } + + // Set the encode format on the still port + + format = still_port->format; + + format->encoding = MMAL_ENCODING_OPAQUE; + format->encoding_variant = MMAL_ENCODING_I420; + + format->es->video.width = VCOS_ALIGN_UP(state->width, 32); + format->es->video.height = VCOS_ALIGN_UP(state->height, 16); + format->es->video.crop.x = 0; + format->es->video.crop.y = 0; + format->es->video.crop.width = state->width; + format->es->video.crop.height = state->height; + format->es->video.frame_rate.num = 0; + format->es->video.frame_rate.den = 1; + + status = mmal_port_format_commit(still_port); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera still format couldn't be set"); + goto error; + } + + /* Ensure there are enough buffers to avoid dropping frames */ + if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) + still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; + + /* Enable component */ + status = mmal_component_enable(camera); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("camera component couldn't be enabled"); + goto error; + } + + raspicamcontrol_set_all_parameters(camera, &state->camera_parameters); + + /* Create pool of buffer headers for the output port to consume */ + pool = mmal_port_pool_create(video_port, video_port->buffer_num, video_port->buffer_size); + + if (!pool) + { + vcos_log_error("Failed to create buffer header pool for camera still port %s", still_port->name); + } + + state->camera_pool = pool; + state->camera_component = camera; + + if (state->verbose) + fprintf(stderr, "Camera component done\n"); + + return status; + +error: + + if (camera) + mmal_component_destroy(camera); + + return status; +} + +/** + * Destroy the camera component + * + * @param state Pointer to state control struct + * + */ +static void destroy_camera_component(RASPIVIDYUV_STATE *state) +{ + if (state->camera_component) + { + mmal_component_destroy(state->camera_component); + state->camera_component = NULL; + } +} + + +/** + * Connect two specific ports together + * + * @param output_port Pointer the output port + * @param input_port Pointer the input port + * @param Pointer to a mmal connection pointer, reassigned if function successful + * @return Returns a MMAL_STATUS_T giving result of operation + * + */ +static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection) +{ + MMAL_STATUS_T status; + + status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT); + + if (status == MMAL_SUCCESS) + { + status = mmal_connection_enable(*connection); + if (status != MMAL_SUCCESS) + mmal_connection_destroy(*connection); + } + + return status; +} + +/** + * Checks if specified port is valid and enabled, then disables it + * + * @param port Pointer the port + * + */ +static void check_disable_port(MMAL_PORT_T *port) +{ + if (port && port->is_enabled) + mmal_port_disable(port); +} + +/** + * Handler for sigint signals + * + * @param signal_number ID of incoming signal. + * + */ +static void signal_handler(int signal_number) +{ + if (signal_number == SIGUSR1) + { + // Handle but ignore - prevents us dropping out if started in none-signal mode + // and someone sends us the USR1 signal anyway + } + else + { + // Going to abort on all other signals + vcos_log_error("Aborting program\n"); + exit(130); + } + +} + +/** + * Pause for specified time, but return early if detect an abort request + * + * @param state Pointer to state control struct + * @param pause Time in ms to pause + * @param callback Struct contain an abort flag tested for early termination + * + */ +static int pause_and_test_abort(RASPIVIDYUV_STATE *state, int pause) +{ + int wait; + + if (!pause) + return 0; + + // Going to check every ABORT_INTERVAL milliseconds + for (wait = 0; wait < pause; wait+= ABORT_INTERVAL) + { + vcos_sleep(ABORT_INTERVAL); + if (state->callback_data.abort) + return 1; + } + + return 0; +} + + +/** + * Function to wait in various ways (depending on settings) + * + * @param state Pointer to the state data + * + * @return !0 if to continue, 0 if reached end of run + */ +static int wait_for_next_change(RASPIVIDYUV_STATE *state) +{ + int keep_running = 1; + static int64_t complete_time = -1; + + // Have we actually exceeded our timeout? + int64_t current_time = vcos_getmicrosecs64()/1000; + + if (complete_time == -1) + complete_time = current_time + state->timeout; + + // if we have run out of time, flag we need to exit + if (current_time >= complete_time && state->timeout != 0) + keep_running = 0; + + switch (state->waitMethod) + { + case WAIT_METHOD_NONE: + (void)pause_and_test_abort(state, state->timeout); + return 0; + + case WAIT_METHOD_FOREVER: + { + // We never return from this. Expect a ctrl-c to exit. + while (1) + // Have a sleep so we don't hog the CPU. + vcos_sleep(10000); + + return 0; + } + + case WAIT_METHOD_TIMED: + { + int abort; + + if (state->bCapturing) + abort = pause_and_test_abort(state, state->onTime); + else + abort = pause_and_test_abort(state, state->offTime); + + if (abort) + return 0; + else + return keep_running; + } + + case WAIT_METHOD_KEYPRESS: + { + char ch; + + if (state->verbose) + fprintf(stderr, "Press Enter to %s, X then ENTER to exit\n", state->bCapturing ? "pause" : "capture"); + + ch = getchar(); + if (ch == 'x' || ch == 'X') + return 0; + else + return keep_running; + } + + case WAIT_METHOD_SIGNAL: + { + // Need to wait for a SIGUSR1 signal + sigset_t waitset; + int sig; + int result = 0; + + sigemptyset( &waitset ); + sigaddset( &waitset, SIGUSR1 ); + + // We are multi threaded because we use mmal, so need to use the pthread + // variant of procmask to block SIGUSR1 so we can wait on it. + pthread_sigmask( SIG_BLOCK, &waitset, NULL ); + + if (state->verbose) + { + fprintf(stderr, "Waiting for SIGUSR1 to %s\n", state->bCapturing ? "pause" : "capture"); + } + + result = sigwait( &waitset, &sig ); + + if (state->verbose && result != 0) + fprintf(stderr, "Bad signal received - error %d\n", errno); + + return keep_running; + } + + } // switch + + return keep_running; +} + +/** + * main + */ +int main(int argc, const char **argv) +{ + // Our main data storage vessel.. + RASPIVIDYUV_STATE state = {0}; + int exit_code = EX_OK; + + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_PORT_T *camera_preview_port = NULL; + MMAL_PORT_T *camera_video_port = NULL; + MMAL_PORT_T *camera_still_port = NULL; + MMAL_PORT_T *preview_input_port = NULL; + + bcm_host_init(); + + // Register our application with the logging system + vcos_log_register("RaspiVid", VCOS_LOG_CATEGORY); + + signal(SIGINT, signal_handler); + + // Disable USR1 for the moment - may be reenabled if go in to signal capture mode + signal(SIGUSR1, SIG_IGN); + + default_status(&state); + + // Do we have any parameters + if (argc == 1) + { + fprintf(stdout, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + + display_valid_parameters(basename(argv[0])); + exit(EX_USAGE); + } + + // Parse the command line and put options in to our status structure + if (parse_cmdline(argc, argv, &state)) + { + status = -1; + exit(EX_USAGE); + } + + if (state.verbose) + { + fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING); + dump_status(&state); + } + + // OK, we have a nice set of parameters. Now set up our components + // We have two components. Camera, Preview + + if ((status = create_camera_component(&state)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create camera component", __func__); + exit_code = EX_SOFTWARE; + } + else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS) + { + vcos_log_error("%s: Failed to create preview component", __func__); + destroy_camera_component(&state); + exit_code = EX_SOFTWARE; + } + else + { + if (state.verbose) + fprintf(stderr, "Starting component connection stage\n"); + + camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT]; + camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT]; + camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT]; + preview_input_port = state.preview_parameters.preview_component->input[0]; + + if (state.preview_parameters.wantPreview ) + { + if (state.verbose) + { + fprintf(stderr, "Connecting camera preview port to preview input port\n"); + fprintf(stderr, "Starting video preview\n"); + } + + // Connect camera to preview + status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection); + + if (status != MMAL_SUCCESS) + state.preview_connection = NULL; + } + else + { + status = MMAL_SUCCESS; + } + + if (status == MMAL_SUCCESS) + { + state.callback_data.file_handle = NULL; + + if (state.filename) + { + if (state.filename[0] == '-') + { + state.callback_data.file_handle = stdout; + + // Ensure we don't upset the output stream with diagnostics/info + state.verbose = 0; + } + else + { + state.callback_data.file_handle = open_filename(&state, state.filename); + } + + if (!state.callback_data.file_handle) + { + // Notify user, carry on but discarding output buffers + vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.filename); + } + } + + state.callback_data.pts_file_handle = NULL; + + if (state.pts_filename) + { + if (state.pts_filename[0] == '-') + { + state.callback_data.pts_file_handle = stdout; + } + else + { + state.callback_data.pts_file_handle = open_filename(&state, state.pts_filename); + if (state.callback_data.pts_file_handle) /* save header for mkvmerge */ + fprintf(state.callback_data.pts_file_handle, "# timecode format v2\n"); + } + + if (!state.callback_data.pts_file_handle) + { + // Notify user, carry on but discarding encoded output buffers + fprintf(stderr, "Error opening output file: %s\nNo output file will be generated\n",state.pts_filename); + state.save_pts=0; + } + } + + // Set up our userdata - this is passed though to the callback where we need the information. + state.callback_data.pstate = &state; + state.callback_data.abort = 0; + + camera_video_port->userdata = (struct MMAL_PORT_USERDATA_T *)&state.callback_data; + + if (state.verbose) + fprintf(stderr, "Enabling camera video port\n"); + + // Enable the camera video port and tell it its callback function + status = mmal_port_enable(camera_video_port, camera_buffer_callback); + + if (status != MMAL_SUCCESS) + { + vcos_log_error("Failed to setup camera output"); + goto error; + } + + if (state.demoMode) + { + // Run for the user specific time.. + int num_iterations = state.timeout / state.demoInterval; + int i; + + if (state.verbose) + fprintf(stderr, "Running in demo mode\n"); + + for (i=0;state.timeout == 0 || iqueue); + int q; + for (q=0;qqueue); + + if (!buffer) + vcos_log_error("Unable to get a required buffer %d from pool queue", q); + + if (mmal_port_send_buffer(camera_video_port, buffer)!= MMAL_SUCCESS) + vcos_log_error("Unable to send a buffer to camera video port (%d)", q); + } + } + + while (running) + { + // Change state + + state.bCapturing = !state.bCapturing; + + if (mmal_port_parameter_set_boolean(camera_video_port, MMAL_PARAMETER_CAPTURE, state.bCapturing) != MMAL_SUCCESS) + { + // How to handle? + } + + if (state.verbose) + { + if (state.bCapturing) + fprintf(stderr, "Starting video capture\n"); + else + fprintf(stderr, "Pausing video capture\n"); + } + + running = wait_for_next_change(&state); + } + + if (state.verbose) + fprintf(stderr, "Finished capture\n"); + } + else + { + if (state.timeout) + vcos_sleep(state.timeout); + else + { + // timeout = 0 so run forever + while(1) + vcos_sleep(ABORT_INTERVAL); + } + } + } + } + else + { + mmal_status_to_int(status); + vcos_log_error("%s: Failed to connect camera to preview", __func__); + } + +error: + + mmal_status_to_int(status); + + if (state.verbose) + fprintf(stderr, "Closing down\n"); + + // Disable all our ports that are not handled by connections + check_disable_port(camera_still_port); + + if (state.preview_parameters.wantPreview && state.preview_connection) + mmal_connection_destroy(state.preview_connection); + + if (state.preview_parameters.preview_component) + mmal_component_disable(state.preview_parameters.preview_component); + + if (state.camera_component) + mmal_component_disable(state.camera_component); + + // Can now close our file. Note disabling ports may flush buffers which causes + // problems if we have already closed the file! + if (state.callback_data.file_handle && state.callback_data.file_handle != stdout) + fclose(state.callback_data.file_handle); + if (state.callback_data.pts_file_handle && state.callback_data.pts_file_handle != stdout) + fclose(state.callback_data.pts_file_handle); + + raspipreview_destroy(&state.preview_parameters); + destroy_camera_component(&state); + + if (state.verbose) + fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n"); + } + + if (status != MMAL_SUCCESS) + raspicamcontrol_check_configuration(128); + + return exit_code; +} + + diff --git a/host_applications/linux/apps/raspicam/gl_scenes/cube_texture_and_coords.h b/host_applications/linux/apps/raspicam/gl_scenes/cube_texture_and_coords.h new file mode 100755 index 0000000..663e23b --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/cube_texture_and_coords.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Spatial coordinates for the cube + +static const GLbyte quadx[6*4*3] = { + /* FRONT */ + -10, -10, 10, + 10, -10, 10, + -10, 10, 10, + 10, 10, 10, + + /* BACK */ + -10, -10, -10, + -10, 10, -10, + 10, -10, -10, + 10, 10, -10, + + /* LEFT */ + -10, -10, 10, + -10, 10, 10, + -10, -10, -10, + -10, 10, -10, + + /* RIGHT */ + 10, -10, -10, + 10, 10, -10, + 10, -10, 10, + 10, 10, 10, + + /* TOP */ + -10, 10, 10, + 10, 10, 10, + -10, 10, -10, + 10, 10, -10, + + /* BOTTOM */ + -10, -10, 10, + -10, -10, -10, + 10, -10, 10, + 10, -10, -10, +}; + +/** Texture coordinates for the quad. */ +static const GLfloat texCoords[6 * 4 * 2] = { + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, +}; + diff --git a/host_applications/linux/apps/raspicam/gl_scenes/mirror.c b/host_applications/linux/apps/raspicam/gl_scenes/mirror.c new file mode 100755 index 0000000..fe14096 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/mirror.c @@ -0,0 +1,123 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mirror.h" +#include "RaspiTex.h" +#include "RaspiTexUtil.h" +#include +#include +#include + +/** + * Draws an external EGL image and applies a sine wave distortion to create + * a hall of mirrors effect. + */ +static RASPITEXUTIL_SHADER_PROGRAM_T mirror_shader = { + .vertex_source = + "attribute vec2 vertex;\n" + "varying vec2 texcoord;" + "void main(void) {\n" + " texcoord = 0.5 * (vertex + 1.0);\n" + " gl_Position = vec4(vertex, 0.0, 1.0);\n" + "}\n", + + .fragment_source = + "#extension GL_OES_EGL_image_external : require\n" + "uniform samplerExternalOES tex;\n" + "uniform float offset;\n" + "const float waves = 2.0;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " float x = texcoord.x + 0.05 * sin(offset + (texcoord.y * waves * 2.0 * 3.141592));\n" + " float y = texcoord.y + 0.05 * sin(offset + (texcoord.x * waves * 2.0 * 3.141592));\n" + " if (y < 1.0 && y > 0.0 && x < 1.0 && x > 0.0) {\n" + " vec2 pos = vec2(x, y);\n" + " gl_FragColor = texture2D(tex, pos);\n" + " }\n" + " else {\n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n" + " }\n" + "}\n", + .uniform_names = {"tex", "offset"}, + .attribute_names = {"vertex"}, +}; + +/** + * Creates the OpenGL ES 2.X context and builds the shaders. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int mirror_init(RASPITEX_STATE *state) +{ + int rc = raspitexutil_gl_init_2_0(state); + if (rc != 0) + goto end; + + rc = raspitexutil_build_shader_program(&mirror_shader); +end: + return rc; +} + +static int mirror_redraw(RASPITEX_STATE *raspitex_state) { + static float offset = 0.0; + + // Start with a clear screen + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Bind the OES texture which is used to render the camera preview + glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->texture); + + offset += 0.05; + GLCHK(glUseProgram(mirror_shader.program)); + GLCHK(glEnableVertexAttribArray(mirror_shader.attribute_locations[0])); + GLfloat varray[] = { + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, -1.0f, + + -1.0f, 1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, + }; + GLCHK(glVertexAttribPointer(mirror_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, varray)); + GLCHK(glUniform1f(mirror_shader.uniform_locations[1], offset)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glDisableVertexAttribArray(mirror_shader.attribute_locations[0])); + GLCHK(glUseProgram(0)); + return 0; +} + +int mirror_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = mirror_init; + state->ops.redraw = mirror_redraw; + state->ops.update_texture = raspitexutil_update_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/mirror.h b/host_applications/linux/apps/raspicam/gl_scenes/mirror.h new file mode 100755 index 0000000..8437728 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/mirror.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MIRROR_H +#define MIRROR_H + +#include "RaspiTex.h" + +int mirror_open(RASPITEX_STATE *state); + +#endif /* MIRROR_H */ diff --git a/host_applications/linux/apps/raspicam/gl_scenes/models.c b/host_applications/linux/apps/raspicam/gl_scenes/models.c new file mode 100755 index 0000000..bbb5a30 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/models.c @@ -0,0 +1,521 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "GLES/gl.h" +#include +#include "EGL/egl.h" +#include "EGL/eglext.h" +#include "models.h" + +#define VMCS_RESOURCE(a,b) (b) + +/****************************************************************************** +Private typedefs, macros and constants +******************************************************************************/ + +enum {VBO_VERTEX, VBO_NORMAL, VBO_TEXTURE, VBO_MAX}; +#define MAX_MATERIALS 4 +#define MAX_MATERIAL_NAME 32 + +typedef struct wavefront_material_s { + GLuint vbo[VBO_MAX]; + int numverts; + char name[MAX_MATERIAL_NAME]; + GLuint texture; +} WAVEFRONT_MATERIAL_T; + +typedef struct wavefront_model_s { + WAVEFRONT_MATERIAL_T material[MAX_MATERIALS]; + int num_materials; + GLuint texture; +} WAVEFRONT_MODEL_T; + + +/****************************************************************************** +Static Data +******************************************************************************/ + +/****************************************************************************** +Static Function Declarations +******************************************************************************/ + +/****************************************************************************** +Static Function Definitions +******************************************************************************/ + +static void create_vbo(GLenum type, GLuint *vbo, int size, void *data) +{ + glGenBuffers(1, vbo); + vc_assert(*vbo); + glBindBuffer(type, *vbo); + glBufferData(type, size, data, GL_STATIC_DRAW); + glBindBuffer(type, 0); +} + + +static void destroy_vbo(GLuint *vbo) +{ + glDeleteBuffers(1, vbo); + *vbo = 0; +} + +#define MAX_VERTICES 100000 +static void *allocbuffer(int size) +{ + return malloc(size); +} + +static void freebuffer(void *p) +{ + free (p); +} + +static void centre_and_rescale(float *verts, int numvertices) +{ + float cx=0.0f, cy=0.0f, cz=0.0f, scale=0.0f; + float minx=0.0f, miny=0.0f, minz=0.0f; + float maxx=0.0f, maxy=0.0f, maxz=0.0f; + int i; + float *v = verts; + minx = maxx = verts[0]; + miny = maxy = verts[1]; + minz = maxz = verts[2]; + for (i=0; i= 3) *dst++ = src[ind + 2]; + indexes += 3; + } +} + +int draw_wavefront(MODEL_T m, GLuint texture) +{ + int i; + WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; + + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + if (mat->texture == -1) continue; + + if (mat->texture) + glBindTexture(GL_TEXTURE_2D, mat->texture); + else + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture); + + if (mat->vbo[VBO_VERTEX]) { + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_VERTEX]); + glVertexPointer(3, GL_FLOAT, 0, NULL); + } + if (mat->vbo[VBO_NORMAL]) { + glEnableClientState(GL_NORMAL_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_NORMAL]); + glNormalPointer(GL_FLOAT, 0, NULL); + } else { + glDisableClientState(GL_NORMAL_ARRAY); + } + if (mat->vbo[VBO_TEXTURE]) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, mat->vbo[VBO_TEXTURE]); + glTexCoordPointer(2, GL_FLOAT, 0, NULL); + } else { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + glDrawArrays(GL_TRIANGLES, 0, mat->numverts); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + return 0; +} + +struct wavefront_model_loading_s { + unsigned short material_index[MAX_MATERIALS]; + int num_materials; + int numv, numt, numn, numf; + unsigned int data[0]; +}; + +static int load_wavefront_obj(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) +{ + char line[256+1]; + unsigned short pp[54+1]; + FILE *fp; + int i, valid; + float *qv = (float *)m->data; + float *qt = (float *)m->data + 3 * MAX_VERTICES; + float *qn = (float *)m->data + (3+2) * MAX_VERTICES; + unsigned short *qf = (unsigned short *)((float *)m->data + (3+2+3) * MAX_VERTICES); + float *pv = qv, *pt = qt, *pn = qn; + unsigned short *pf = qf; + fp = fopen(modelname, "r"); + if (!fp) return -1; + + m->num_materials = 0; + m->material_index[0] = 0; + + valid = fread(line, 1, sizeof(line)-1, fp); + + while (valid > 0) { + char *s, *end = line; + + while((end-line < valid) && *end != '\n' && *end != '\r') + end++; + *end++ = 0; + + if((end-line < valid) && *end != '\n' && *end != '\r') + *end++ = 0; + + s = line; + + if (s[strlen(s)-1] == 10) s[strlen(s)-1]=0; + switch (s[0]) { + case '#': break; // comment + case '\r': case '\n': case '\0': break; // blank line + case 'm': vc_assert(strncmp(s, "mtllib", sizeof "mtllib"-1)==0); break; + case 'o': break; + case 'u': + if (sscanf(s, "usemtl %s", /*MAX_MATERIAL_NAME-1, */model->material[m->num_materials].name) == 1) { + if (m->num_materials < MAX_MATERIALS) { + if (m->num_materials > 0 && ((pf-qf)/3 == m->material_index[m->num_materials-1] || strcmp(model->material[m->num_materials-1].name, model->material[m->num_materials].name)==0)) { + strcpy(model->material[m->num_materials-1].name, model->material[m->num_materials].name); + m->num_materials--; + } else + m->material_index[m->num_materials] = (pf-qf)/3; + m->num_materials++; + } + } else { printf("%s", s); vc_assert(0); } + break; + case 'g': vc_assert(strncmp(s, "g ", sizeof "g "-1)==0); break; + case 's': vc_assert(strncmp(s, "s ", sizeof "s "-1)==0); break; + case 'v': case 'f': + if (sscanf(s, "v %f %f %f", pv+0, pv+1, pv+2) == 3) { + pv += 3; + } else if (sscanf(s, "vt %f %f", pt+0, pt+1) == 2) { + pt += 2; + } else if (sscanf(s, "vn %f %f %f", pn+0, pn+1, pn+2) == 3) { + pn += 3; + } else if (i = sscanf(s, "f"" %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" + " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu" + " %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu//%hu %hu", + pp+ 0, pp+ 1, pp+ 2, pp+ 3, pp+ 4, pp+ 5, pp+ 6, pp+ 7, pp+ 8, pp+ 9, pp+10, pp+11, + pp+12, pp+13, pp+14, pp+15, pp+16, pp+17, pp+18, pp+19, pp+20, pp+21, pp+22, pp+23, + pp+24, pp+25, pp+26, pp+27, pp+28, pp+29, pp+30, pp+32, pp+32, pp+33, pp+34, pp+35, pp+36), i >= 6) { + int poly = i/2; + //vc_assert(i < countof(pp)); // may need to increment poly count and pp array + for (i=1; i= 6) { + int poly = i/2; + //vc_assert(i < countof(pp); // may need to increment poly count and pp array + for (i=1; i= 9) { + int poly = i/3; + //vc_assert(i < countof(pp); // may need to increment poly count and pp array + for (i=1; i valid ? valid : end-line; + memmove(line, end, valid - i); + valid -= i; + valid += fread(line+valid, 1, sizeof(line)-1-valid, fp); + } + fclose(fp); + + if (m->num_materials==0) m->material_index[m->num_materials++] = 0; + + centre_and_rescale(qv, (pv-qv)/3); + renormalise(qn, (pn-qn)/3); + //centre_and_rescale2(qt, (pt-qt)/2); + + m->numv = pv-qv; + m->numt = pt-qt; + m->numn = pn-qn; + m->numf = pf-qf; + + // compress array + //memcpy((float *)m->data, (float *)m->data, m->numv * sizeof *qv); - nop + memcpy((float *)m->data + m->numv, (float *)m->data + 3 * MAX_VERTICES, m->numt * sizeof *qt); + memcpy((float *)m->data + m->numv + m->numt,(float *) m->data + (3 + 2) * MAX_VERTICES, m->numn * sizeof *qn); + memcpy((float *)m->data + m->numv + m->numt + m->numn, (float *)m->data + (3 + 2 + 3) * MAX_VERTICES, m->numf * sizeof *qf); + + return 0; +} + +static int load_wavefront_dat(const char *modelname, WAVEFRONT_MODEL_T *model, struct wavefront_model_loading_s *m) +{ + FILE *fp; + int s; + const int size = sizeof *m + + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*MAX_VERTICES; //each face has 9 vertices + + fp = fopen(modelname, "r"); + if (!fp) return -1; + s = fread(m, 1, size, fp); + if (s < 0) return -1; + fclose(fp); + return 0; +} + +MODEL_T load_wavefront(const char *modelname, const char *texturename) +{ + WAVEFRONT_MODEL_T *model; + float *temp, *qv, *qt, *qn; + unsigned short *qf; + int i; + int numverts = 0, offset = 0; + struct wavefront_model_loading_s *m; + int s=-1; + char modelname_obj[128]; + model = malloc(sizeof *model); + if (!model || !modelname) return NULL; + memset (model, 0, sizeof *model); + model->texture = 0; //load_texture(texturename); + m = allocbuffer(sizeof *m + + sizeof(float)*(3+2+3)*MAX_VERTICES + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*MAX_VERTICES); //each face has 9 vertices + if (!m) return 0; + + if (strlen(modelname) + 5 <= sizeof modelname_obj) { + strcpy(modelname_obj, modelname); + strcat(modelname_obj, ".dat"); + s = load_wavefront_dat(modelname_obj, model, m); + } + if (s==0) {} + else if (strncmp(modelname + strlen(modelname) - 4, ".obj", 4) == 0) { + #ifdef DUMP_OBJ_DAT + int size; + FILE *fp; + #endif + s = load_wavefront_obj(modelname, model, m); + #ifdef DUMP_OBJ_DAT + strcpy(modelname_obj, modelname); + strcat(modelname_obj, ".dat"); + size = sizeof *m + + sizeof(float)*(3*m->numv+2*m->numt+3*m->numn) + // 3 vertices + 2 textures + 3 normals + sizeof(unsigned short)*3*m->numf; //each face has 9 vertices + fp = host_file_open(modelname_obj, "w"); + fwrite(m, 1, size, fp); + fclose(fp); + #endif + } else if (strncmp(modelname + strlen(modelname) - 4, ".dat", 4) == 0) { + s = load_wavefront_dat(modelname, model, m); + } + if (s != 0) return 0; + + qv = (float *)(m->data); + qt = (float *)(m->data + m->numv); + qn = (float *)(m->data + m->numv + m->numt); + qf = (unsigned short *)(m->data + m->numv + m->numt + m->numn); + + numverts = m->numf/3; + vc_assert(numverts <= MAX_VERTICES); + + temp = allocbuffer(3*numverts*sizeof *temp); + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + mat->numverts = i < m->num_materials-1 ? m->material_index[i+1]-m->material_index[i] : numverts - m->material_index[i]; + // vertex, texture, normal + deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 + + deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 + + deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 + offset += mat->numverts; + mat->texture = model->texture; + } + model->num_materials = m->num_materials; + vc_assert(offset == numverts); + freebuffer(temp); + freebuffer(m); + return (MODEL_T)model; +} + +void unload_wavefront(MODEL_T m) +{ + WAVEFRONT_MODEL_T *model = (WAVEFRONT_MODEL_T *)m; + int i; + for (i=0; inum_materials; i++) { + WAVEFRONT_MATERIAL_T *mat = model->material + i; + if (mat->vbo[VBO_VERTEX]) + destroy_vbo(mat->vbo+VBO_VERTEX); + if (mat->vbo[VBO_TEXTURE]) + destroy_vbo(mat->vbo+VBO_TEXTURE); + if (mat->vbo[VBO_NORMAL]) + destroy_vbo(mat->vbo+VBO_NORMAL); + } +} + +// create a cube model that looks like a wavefront model, +MODEL_T cube_wavefront(void) +{ + static const float qv[] = { + -0.5f, -0.5f, 0.5f, + -0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, 0.5f, + -0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, -0.5f, + -0.5f, 0.5f, -0.5f, + }; + + static const float qn[] = { + 0.0f, -1.0f, -0.0f, + 0.0f, 1.0f, -0.0f, + 0.0f, 0.0f, 1.0f, + 1.0f, 0.0f, -0.0f, + 0.0f, 0.0f, -1.0f, + -1.0f, 0.0f, -0.0f, + }; + + static const float qt[] = { + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + }; + + static const unsigned short qf[] = { + 1,1,1, 2,2,1, 3,3,1, + 3,3,1, 4,4,1, 1,1,1, + 5,4,2, 6,1,2, 7,2,2, + 7,2,2, 8,3,2, 5,4,2, + 1,4,3, 4,1,3, 6,2,3, + 6,2,3, 5,3,3, 1,4,3, + 4,4,4, 3,1,4, 7,2,4, + 7,2,4, 6,3,4, 4,4,4, + 3,4,5, 2,1,5, 8,2,5, + 8,2,5, 7,3,5, 3,4,5, + 2,4,6, 1,1,6, 5,2,6, + 5,2,6, 8,3,6, 2,4,6, + }; + WAVEFRONT_MODEL_T *model = malloc(sizeof *model); + if (model) { + WAVEFRONT_MATERIAL_T *mat = model->material; + float *temp; + const int offset = 0; + memset(model, 0, sizeof *model); + + temp = allocbuffer(3*MAX_VERTICES*sizeof *temp); + mat->numverts = countof(qf)/3; + // vertex, texture, normal + deindex(temp, qv, qf+3*offset+0, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_VERTEX, 3 * mat->numverts * sizeof *qv, temp); // 3 + + deindex(temp, qt, qf+3*offset+1, 2, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_TEXTURE, 2 * mat->numverts * sizeof *qt, temp); // 2 + + deindex(temp, qn, qf+3*offset+2, 3, mat->numverts); + create_vbo(GL_ARRAY_BUFFER, mat->vbo+VBO_NORMAL, 3 * mat->numverts * sizeof *qn, temp); // 3 + + freebuffer(temp); + model->num_materials = 1; + } + return (MODEL_T)model; +} + + diff --git a/host_applications/linux/apps/raspicam/gl_scenes/models.h b/host_applications/linux/apps/raspicam/gl_scenes/models.h new file mode 100755 index 0000000..4a6cbd0 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/models.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef MODELS_T +#define MODELS_T +typedef struct opqaue_model_s * MODEL_T; + +MODEL_T load_wavefront(const char *modelname, const char *texturename); +MODEL_T cube_wavefront(void); +void unload_wavefront(MODEL_T m); +int draw_wavefront(MODEL_T m, GLuint texture); +#endif diff --git a/host_applications/linux/apps/raspicam/gl_scenes/sobel.c b/host_applications/linux/apps/raspicam/gl_scenes/sobel.c new file mode 100755 index 0000000..ad2c8d9 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/sobel.c @@ -0,0 +1,192 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "sobel.h" +#include "RaspiTex.h" +#include "RaspiTexUtil.h" +#include +#include +#include + +/* \file sobel.c + * Example code for implementing Sobel filter as GLSL shaders. + * The input image is a greyscale texture from the MMAL buffer Y plane. + */ + +#define SOBEL_VSHADER_SOURCE \ + "attribute vec2 vertex;\n" \ + "varying vec2 texcoord;\n" \ + "void main(void) {\n" \ + " texcoord = 0.5 * (vertex + 1.0);\n" \ + " gl_Position = vec4(vertex, 0.0, 1.0);\n" \ + "}\n" + +/* Example Sobel edge detct shader. The texture format for + * EGL_IMAGE_BRCM_MULTIMEDIA_Y is a one byte per pixel greyscale GL_LUMINANCE. + * If the output is to be fed into another image processing shader then it may + * be worth changing this code to take 4 input Y pixels and pack the result + * into a 32bpp RGBA pixel. + */ +#define SOBEL_FSHADER_SOURCE \ + "#extension GL_OES_EGL_image_external : require\n" \ + "uniform samplerExternalOES tex;\n" \ + "varying vec2 texcoord;\n" \ + "uniform vec2 tex_unit;\n" \ + "void main(void) {\n" \ + " float x = texcoord.x;\n" \ + " float y = texcoord.y;\n" \ + " float x1 = x - tex_unit.x;\n" \ + " float y1 = y - tex_unit.y;\n" \ + " float x2 = x + tex_unit.x;\n" \ + " float y2 = y + tex_unit.y;\n" \ + " vec4 p0 = texture2D(tex, vec2(x1, y1));\n" \ + " vec4 p1 = texture2D(tex, vec2(x, y1));\n" \ + " vec4 p2 = texture2D(tex, vec2(x2, y1));\n" \ + " vec4 p3 = texture2D(tex, vec2(x1, y));\n" \ + " /* vec4 p4 = texture2D(tex, vec2(x, y)); */\n" \ + " vec4 p5 = texture2D(tex, vec2(x2, y));\n" \ + " vec4 p6 = texture2D(tex, vec2(x1, y2));\n" \ + " vec4 p7 = texture2D(tex, vec2(x, y2));\n" \ + " vec4 p8 = texture2D(tex, vec2(x2, y2));\n" \ + "\n" \ + " vec4 v = p0 + (2.0 * p1) + p3 -p6 + (-2.0 * p7) + -p8;\n" \ + " vec4 h = p0 + (2.0 * p3) + p7 -p2 + (-2.0 * p5) + -p8;\n" \ + " gl_FragColor = sqrt(h*h + v*v);\n" \ + " gl_FragColor.a = 1.0;\n" \ + "}\n" + +static GLfloat quad_varray[] = { + -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, +}; + +static GLuint quad_vbo; + +static RASPITEXUTIL_SHADER_PROGRAM_T sobel_shader = +{ + .vertex_source = SOBEL_VSHADER_SOURCE, + .fragment_source = SOBEL_FSHADER_SOURCE, + .uniform_names = {"tex", "tex_unit"}, + .attribute_names = {"vertex"}, +}; + +static const EGLint sobel_egl_config_attribs[] = +{ + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE +}; + + +/** + * Initialisation of shader uniforms. + * + * @param width Width of the EGL image. + * @param width Height of the EGL image. + */ +static int shader_set_uniforms(RASPITEXUTIL_SHADER_PROGRAM_T *shader, + int width, int height) +{ + GLCHK(glUseProgram(shader->program)); + GLCHK(glUniform1i(shader->uniform_locations[0], 0)); // Texture unit + + /* Dimensions of a single pixel in texture co-ordinates */ + GLCHK(glUniform2f(shader->uniform_locations[1], + 1.0 / (float) width, 1.0 / (float) height)); + + /* Enable attrib 0 as vertex array */ + GLCHK(glEnableVertexAttribArray(shader->attribute_locations[0])); + return 0; +} + +/** + * Creates the OpenGL ES 2.X context and builds the shaders. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int sobel_init(RASPITEX_STATE *raspitex_state) +{ + int rc = 0; + int width = raspitex_state->width; + int height = raspitex_state->height; + + vcos_log_trace("%s", VCOS_FUNCTION); + raspitex_state->egl_config_attribs = sobel_egl_config_attribs; + rc = raspitexutil_gl_init_2_0(raspitex_state); + if (rc != 0) + goto end; + + rc = raspitexutil_build_shader_program(&sobel_shader); + if (rc != 0) + goto end; + + rc = shader_set_uniforms(&sobel_shader, width, height); + if (rc != 0) + goto end; + + GLCHK(glGenBuffers(1, &quad_vbo)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glBufferData(GL_ARRAY_BUFFER, sizeof(quad_varray), quad_varray, GL_STATIC_DRAW)); + GLCHK(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); + +end: + return rc; +} + +/* Redraws the scene with the latest luma buffer. + * + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int sobel_redraw(RASPITEX_STATE* state) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GLCHK(glUseProgram(sobel_shader.program)); + + /* Bind the Y plane texture */ + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, state->y_texture)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glEnableVertexAttribArray(sobel_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(sobel_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + return 0; +} + +int sobel_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = sobel_init; + state->ops.redraw = sobel_redraw; + state->ops.update_y_texture = raspitexutil_update_y_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/sobel.h b/host_applications/linux/apps/raspicam/gl_scenes/sobel.h new file mode 100755 index 0000000..14ab13a --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/sobel.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SOBEL_H +#define SOBEL_H + +#include "RaspiTex.h" + +int sobel_open(RASPITEX_STATE *state); + +#endif /* SOBEL_H */ diff --git a/host_applications/linux/apps/raspicam/gl_scenes/square.c b/host_applications/linux/apps/raspicam/gl_scenes/square.c new file mode 100755 index 0000000..a1028a1 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/square.c @@ -0,0 +1,121 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include "RaspiTexUtil.h" + +/* Vertex co-ordinates: + * + * v0----v1 + * | | + * | | + * | | + * v3----v2 + */ + +static const GLfloat vertices[] = +{ +#define V0 -0.8, 0.8, 0.8, +#define V1 0.8, 0.8, 0.8, +#define V2 0.8, -0.8, 0.8, +#define V3 -0.8, -0.8, 0.8, + V0 V3 V2 V2 V1 V0 +}; + +/* Texture co-ordinates: + * + * (0,0) b--c + * | | + * a--d + * + * b,a,d d,c,b + */ +static const GLfloat tex_coords[] = +{ + 0, 0, 0, 1, 1, 1, + 1, 1, 1, 0, 0, 0 +}; + +static GLfloat angle; +static uint32_t anim_step; + +static int square_init(RASPITEX_STATE *state) +{ + int rc = raspitexutil_gl_init_1_0(state); + + if (rc != 0) + goto end; + + angle = 0.0f; + anim_step = 0; + + glClearColor(0, 0, 0, 0); + glClearDepthf(1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + +end: + return rc; +} + +static int square_update_model(RASPITEX_STATE *state) +{ + int frames_per_rev = 30 * 15; + (void) state; + angle = (anim_step * 360) / (GLfloat) frames_per_rev; + anim_step = (anim_step + 1) % frames_per_rev; + + return 0; +} + +static int square_redraw(RASPITEX_STATE *state) +{ + /* Bind the OES texture which is used to render the camera preview */ + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, state->texture)); + glLoadIdentity(); + glRotatef(angle, 0.0, 0.0, 1.0); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, vertices); + glDisableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, tex_coords); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, vcos_countof(tex_coords) / 2)); + return 0; +} + +int square_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = square_init; + state->ops.update_model = square_update_model; + state->ops.redraw = square_redraw; + state->ops.update_texture = raspitexutil_update_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/square.h b/host_applications/linux/apps/raspicam/gl_scenes/square.h new file mode 100755 index 0000000..ef4741a --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/square.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SQUARE_H +#define SQUARE_H + +#include "RaspiTex.h" + +int square_open(RASPITEX_STATE *state); + +#endif /* SQUARE_H */ diff --git a/host_applications/linux/apps/raspicam/gl_scenes/teapot.c b/host_applications/linux/apps/raspicam/gl_scenes/teapot.c new file mode 100755 index 0000000..eac4bcb --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/teapot.c @@ -0,0 +1,332 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, OtherCrashOverride +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// A rotating cube rendered with OpenGL|ES. Three images used as textures on the cube faces. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cube_texture_and_coords.h" +#include "models.h" +#include "teapot.h" + +#include "RaspiTex.h" +#include "RaspiTexUtil.h" + +#define PATH "./" + +#ifndef M_PI + #define M_PI 3.141592654 +#endif + +typedef struct +{ + uint32_t screen_width; + uint32_t screen_height; + GLuint tex; +// model rotation vector and direction + GLfloat rot_angle_x_inc; + GLfloat rot_angle_y_inc; + GLfloat rot_angle_z_inc; +// current model rotation angles + GLfloat rot_angle_x; + GLfloat rot_angle_y; + GLfloat rot_angle_z; +// current distance from camera + GLfloat distance; + GLfloat distance_inc; + MODEL_T model; +} TEAPOT_STATE_T; + +static void init_ogl(TEAPOT_STATE_T *state); +static void init_model_proj(TEAPOT_STATE_T *state); +static void reset_model(TEAPOT_STATE_T *state); +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc); +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc); + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void init_ogl(TEAPOT_STATE_T *state) +{ + // Set background color and clear buffers + glClearColor((0.3922f+7*0.5f)/8, (0.1176f+7*0.5f)/8, (0.5882f+7*0.5f)/8, 1.0f); + + // Enable back face culling. + glEnable(GL_CULL_FACE); + + glEnable(GL_DEPTH_TEST); + glClearDepthf(1.0); + glDepthFunc(GL_LEQUAL); + + float noAmbient[] = {1.0f, 1.0f, 1.0f, 1.0f}; + glLightfv(GL_LIGHT0, GL_AMBIENT, noAmbient); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHTING); +} + +/*********************************************************** + * Name: init_model_proj + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Sets the OpenGL|ES model to default values + * + * Returns: void + * + ***********************************************************/ +static void init_model_proj(TEAPOT_STATE_T *state) +{ + float nearp = 0.1f; + float farp = 500.0f; + float hht; + float hwd; + + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + + glViewport(0, 0, (GLsizei)state->screen_width, (GLsizei)state->screen_height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + hht = nearp * (float)tan(45.0 / 2.0 / 180.0 * M_PI); + hwd = hht * (float)state->screen_width / (float)state->screen_height; + + glFrustumf(-hwd, hwd, -hht, hht, nearp, farp); + + glEnableClientState( GL_VERTEX_ARRAY ); + + reset_model(state); +} + +/*********************************************************** + * Name: reset_model + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Resets the Model projection and rotation direction + * + * Returns: void + * + ***********************************************************/ +static void reset_model(TEAPOT_STATE_T *state) +{ + // reset model position + glMatrixMode(GL_MODELVIEW); + + // reset model rotation + state->rot_angle_x = 45.f; state->rot_angle_y = 30.f; state->rot_angle_z = 0.f; + state->rot_angle_x_inc = 0.5f; state->rot_angle_y_inc = 0.5f; state->rot_angle_z_inc = 0.f; + state->distance = 0.8f*1.5f; +} + +/*********************************************************** + * Name: teapot_update_model + * + * Arguments: + * TEAPOT_STATE_T *state - holds OGLES model info + * + * Description: Updates model projection to current position/rotation + * + * Returns: void + * + ***********************************************************/ +static int teapot_update_model(RASPITEX_STATE *raspitex_state) +{ + TEAPOT_STATE_T *state = (TEAPOT_STATE_T *) raspitex_state->scene_state; + + // update position + state->rot_angle_x = inc_and_wrap_angle(state->rot_angle_x, state->rot_angle_x_inc); + state->rot_angle_y = inc_and_wrap_angle(state->rot_angle_y, state->rot_angle_y_inc); + state->rot_angle_z = inc_and_wrap_angle(state->rot_angle_z, state->rot_angle_z_inc); + state->distance = inc_and_clip_distance(state->distance, state->distance_inc); + + glLoadIdentity(); + // move camera back to see the cube + glTranslatef(0.f, 0.f, -state->distance); + + // Rotate model to new position + glRotatef(state->rot_angle_x, 1.f, 0.f, 0.f); + glRotatef(state->rot_angle_y, 0.f, 1.f, 0.f); + glRotatef(state->rot_angle_z, 0.f, 0.f, 1.f); + + return 0; +} + +/*********************************************************** + * Name: inc_and_wrap_angle + * + * Arguments: + * GLfloat angle current angle + * GLfloat angle_inc angle increment + * + * Description: Increments or decrements angle by angle_inc degrees + * Wraps to 0 at 360 deg. + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_wrap_angle(GLfloat angle, GLfloat angle_inc) +{ + angle += angle_inc; + + if (angle >= 360.0) + angle -= 360.f; + else if (angle <=0) + angle += 360.f; + + return angle; +} + +/*********************************************************** + * Name: inc_and_clip_distance + * + * Arguments: + * GLfloat distance current distance + * GLfloat distance_inc distance increment + * + * Description: Increments or decrements distance by distance_inc units + * Clips to range + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat inc_and_clip_distance(GLfloat distance, GLfloat distance_inc) +{ + distance += distance_inc; + + if (distance >= 10.0f) + distance = 10.f; + else if (distance <= 1.0f) + distance = 1.0f; + + return distance; +} + +/*********************************************************** + * Name: teapot_redraw + * + * Arguments: + * RASPITEX_STATE_T *state - holds OGLES model info + * + * Description: Draws the model + * + * Returns: void + * + ***********************************************************/ +static int teapot_redraw(RASPITEX_STATE *raspitex_state) +{ + TEAPOT_STATE_T *state = (TEAPOT_STATE_T *) raspitex_state->scene_state; + + // Start with a clear screen + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* Bind the OES texture which is used to render the camera preview */ + glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->texture); + draw_wavefront(state->model, raspitex_state->texture); + return 0; +} + +//============================================================================== + +static int teapot_gl_init(RASPITEX_STATE *raspitex_state) +{ + const char *model_path = "/opt/vc/src/hello_pi/hello_teapot/teapot.obj.dat"; + TEAPOT_STATE_T *state = NULL; + int rc = 0; + + // Clear scene state + state = calloc(1, sizeof(TEAPOT_STATE_T)); + raspitex_state->scene_state = state; + state->screen_width = raspitex_state->width; + state->screen_height = raspitex_state->height; + + rc = raspitexutil_gl_init_1_0(raspitex_state); + if (rc != 0) + goto end; + + // Start OGLES + init_ogl(state); + + // Setup the model world + init_model_proj(state); + state->model = load_wavefront(model_path, NULL); + + if (! state->model) + { + vcos_log_error("Failed to load model from %s\n", model_path); + rc = -1; + } + +end: + return rc; +} + +static void teapot_gl_term(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s:", VCOS_FUNCTION); + + TEAPOT_STATE_T *state = raspitex_state->scene_state; + if (state) + { + if (state->model) + unload_wavefront(state->model); + raspitexutil_gl_term(raspitex_state); + free(raspitex_state->scene_state); + raspitex_state->scene_state = NULL; + } +} + +int teapot_open(RASPITEX_STATE *raspitex_state) +{ + raspitex_state->ops.gl_init = teapot_gl_init; + raspitex_state->ops.update_model = teapot_update_model; + raspitex_state->ops.redraw = teapot_redraw; + raspitex_state->ops.gl_term = teapot_gl_term; + raspitex_state->ops.update_texture = raspitexutil_update_texture; + return 0; +} + diff --git a/host_applications/linux/apps/raspicam/gl_scenes/teapot.h b/host_applications/linux/apps/raspicam/gl_scenes/teapot.h new file mode 100755 index 0000000..c8d5162 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/teapot.h @@ -0,0 +1,35 @@ +/* +Copyright (c) 2012-2013, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef TEAPOT_H +#define TEAPOT_H + +#include "RaspiTex.h" + +int teapot_open(RASPITEX_STATE *raspitex_state); + +#endif /* TEAPOT_H */ diff --git a/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.c b/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.c new file mode 100755 index 0000000..f47b83e --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.c @@ -0,0 +1,332 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2016, Tim Gover +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Make the render output CPU accessible by defining a framebuffer texture + * stored in a VCSM (VideoCore shared memory) EGL image. + * + * This example just demonstrates how to use use the APIs by using the CPU. + * to blit an animated rectangle into frame-buffer texture in shared memory. + * + * A more realistic example would be to do a blur, edge-detect in GLSL then pass + * the buffer to OpenCV. There may be some benefit in using multiple GL contexts + * to reduce the impact of serializing operations with a glFlush. + * + * N.B VCSM textures are raster scan order textures. This makes it very + * convenient to read and modify VCSM frame-buffer textures from the CPU. + * However, if the output of the CPU stage is drawn again as a texture that + * is rotated or scaled then it can sometimes be better to use glTexImage2D + * to allow the driver to convert this back into the native texture format. + * + * Example usage + * raspistill -p 0,0,1024,1024 -gw 0,0,1024,1024 -t 10000 --gl -gs vcsm_square + */ +/* Uncomment the next line to compare with the glReadPixels implementation. VCSM + * should run at about 40fps with a 1024x1024 texture compared to about 20fps + * using glReadPixels. + */ +//#define USE_READPIXELS + +#include "vcsm_square.h" +#include +#include +#include +#include "RaspiTex.h" +#include "RaspiTexUtil.h" +#include "user-vcsm.h" + +/* Draw a scaled quad showing the entire texture with the + * origin defined as an attribute */ +static RASPITEXUTIL_SHADER_PROGRAM_T vcsm_square_oes_shader = +{ + .vertex_source = + "attribute vec2 vertex;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " texcoord = 0.5 * (vertex + 1.0);\n" \ + " gl_Position = vec4(vertex, 0.0, 1.0);\n" + "}\n", + + .fragment_source = + "#extension GL_OES_EGL_image_external : require\n" + "uniform samplerExternalOES tex;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(tex, texcoord);\n" + "}\n", + .uniform_names = {"tex"}, + .attribute_names = {"vertex"}, +}; +static RASPITEXUTIL_SHADER_PROGRAM_T vcsm_square_shader = +{ + .vertex_source = + "attribute vec2 vertex;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " texcoord = 0.5 * (vertex + 1.0);\n" \ + " gl_Position = vec4(vertex, 0.0, 1.0);\n" + "}\n", + + .fragment_source = + "uniform sampler2D tex;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(tex, texcoord);\n" + "}\n", + .uniform_names = {"tex"}, + .attribute_names = {"vertex"}, +}; + +static GLfloat quad_varray[] = { + -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, +}; + +static GLuint quad_vbo; + +#ifdef USE_READPIXELS +unsigned char *pixel_buffer; +#else +static struct egl_image_brcm_vcsm_info vcsm_info; +static EGLImageKHR eglFbImage; +#endif +static GLuint fb_tex_name; +static GLuint fb_name; + + +// VCSM buffer dimensions must be a power of two. Use glViewPort to draw NPOT +// rectangles within the VCSM buffer. +static int fb_width = 1024; +static int fb_height = 1024; + +static const EGLint vcsm_square_egl_config_attribs[] = +{ + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE +}; + +static int vcsm_square_init(RASPITEX_STATE *raspitex_state) +{ + int rc = vcsm_init(); + vcos_log_trace("%s: vcsm_init %d", VCOS_FUNCTION, rc); + + raspitex_state->egl_config_attribs = vcsm_square_egl_config_attribs; + rc = raspitexutil_gl_init_2_0(raspitex_state); + + if (rc != 0) + goto end; + + // Shader for drawing the YUV OES texture + rc = raspitexutil_build_shader_program(&vcsm_square_oes_shader); + GLCHK(glUseProgram(vcsm_square_oes_shader.program)); + GLCHK(glUniform1i(vcsm_square_oes_shader.uniform_locations[0], 0)); // tex unit + + // Shader for drawing VCSM sampler2D texture + rc = raspitexutil_build_shader_program(&vcsm_square_shader); + GLCHK(glUseProgram(vcsm_square_shader.program)); + GLCHK(glUniform1i(vcsm_square_shader.uniform_locations[0], 0)); // tex unit + + GLCHK(glGenFramebuffers(1, &fb_name)); + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name)); + + GLCHK(glGenTextures(1, &fb_tex_name)); + GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name)); + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + +#ifdef USE_READPIXELS + printf("Using glReadPixels\n"); + GLCHK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fb_width, fb_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); + pixel_buffer = malloc(fb_width * fb_height * 4); + if (! pixel_buffer) { + rc = -1; + goto end; + } +#else /* USE_READPIXELS */ + printf("Using VCSM\n"); + vcsm_info.width = fb_width; + vcsm_info.height = fb_height; + eglFbImage = eglCreateImageKHR(raspitex_state->display, EGL_NO_CONTEXT, + EGL_IMAGE_BRCM_VCSM, &vcsm_info, NULL); + if (eglFbImage == EGL_NO_IMAGE_KHR || vcsm_info.vcsm_handle == 0) { + vcos_log_error("%s: Failed to create EGL VCSM image\n", VCOS_FUNCTION); + rc = -1; + goto end; + } + + GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglFbImage)); +#endif /* USE_READPIXELS */ + + GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_tex_name, 0)); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + vcos_log_error("GL_FRAMEBUFFER is not complete\n"); + rc = -1; + goto end; + } + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + GLCHK(glGenBuffers(1, &quad_vbo)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glBufferData(GL_ARRAY_BUFFER, sizeof(quad_varray), quad_varray, GL_STATIC_DRAW)); + + GLCHK(glClearColor(0, 0, 0, 0)); +end: + return rc; +} + +// Write the shared memory texture writing something to each line. This is +// just to show that the buffer really is CPU modifiable. +static void vcsm_square_draw_pattern(unsigned char *buffer) +{ + static unsigned x_offset; + + unsigned char *line_start = (unsigned char *) buffer; + unsigned width = fb_width > 32 ? 32 : fb_width; + int i = 0; + size_t stride = fb_width << 2; + + x_offset = (x_offset + 1) % (fb_width - width); + for (i = 0; i < fb_height; i++) { + memset(line_start + (x_offset << 2), ~0, width << 2); + line_start += stride; + } +} + +#ifdef USE_READPIXELS +static int vcsm_square_redraw_readpixels(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name)); + GLCHK(glViewport(0,0,fb_width,fb_height)); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Fill the viewport with the camFill the viewport with the camera image + GLCHK(glUseProgram(vcsm_square_oes_shader.program)); + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->y_texture)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glEnableVertexAttribArray(vcsm_square_oes_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(vcsm_square_oes_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glReadPixels(0, 0, fb_width, fb_height, GL_RGBA, GL_UNSIGNED_BYTE, pixel_buffer)); + + vcsm_square_draw_pattern(pixel_buffer); + + // Enable default window surface + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + + // Draw the modified texture buffer to the screen + GLCHK(glViewport(raspitex_state->x, raspitex_state->y, raspitex_state->width, raspitex_state->height)); + GLCHK(glUseProgram(vcsm_square_shader.program)); + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name)); + GLCHK(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_width, fb_height, GL_RGBA, GL_UNSIGNED_BYTE, pixel_buffer)); + GLCHK(glEnableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(vcsm_square_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glDisableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); + GLCHK(glUseProgram(0)); + + return 0; +} +#else /* USE_READPIXELS */ +static int vcsm_square_redraw(RASPITEX_STATE *raspitex_state) +{ + unsigned char *vcsm_buffer = NULL; + VCSM_CACHE_TYPE_T cache_type; + + vcos_log_trace("%s", VCOS_FUNCTION); + + glClearColor(255, 0, 0, 255); + + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name)); + GLCHK(glViewport(0, 0, fb_width, fb_height)); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Fill the viewport with the camFill the viewport with the camera image + GLCHK(glUseProgram(vcsm_square_oes_shader.program)); + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->y_texture)); + GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); + GLCHK(glEnableVertexAttribArray(vcsm_square_oes_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(vcsm_square_oes_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glFinish()); + + // Make the buffer CPU addressable with host cache enabled + vcsm_buffer = (unsigned char *) vcsm_lock_cache(vcsm_info.vcsm_handle, VCSM_CACHE_TYPE_HOST, &cache_type); + if (! vcsm_buffer) { + vcos_log_error("Failed to lock VCSM buffer for handle %d\n", vcsm_info.vcsm_handle); + return -1; + } + vcos_log_trace("Locked vcsm handle %d at %p\n", vcsm_info.vcsm_handle, vcsm_buffer); + + vcsm_square_draw_pattern(vcsm_buffer); + + // Release the locked texture memory to flush the CPU cache and allow GPU + // to read it + vcsm_unlock_ptr(vcsm_buffer); + + // Enable default window surface + GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + + // Draw the modified texture buffer to the screen + GLCHK(glViewport(raspitex_state->x, raspitex_state->y, raspitex_state->width, raspitex_state->height)); + GLCHK(glUseProgram(vcsm_square_shader.program)); + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name)); + GLCHK(glEnableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(vcsm_square_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glDisableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); + GLCHK(glUseProgram(0)); + + return 0; +} +#endif /* USE_READPIXELS */ + +int vcsm_square_open(RASPITEX_STATE *raspitex_state) +{ + vcos_log_trace("%s", VCOS_FUNCTION); + + raspitex_state->ops.gl_init = vcsm_square_init; +#ifdef USE_READPIXELS + raspitex_state->ops.redraw = vcsm_square_redraw_readpixels; +#else + raspitex_state->ops.redraw = vcsm_square_redraw; +#endif /* USE_READPIXELS */ + raspitex_state->ops.update_y_texture = raspitexutil_update_y_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.h b/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.h new file mode 100755 index 0000000..632d640 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2016, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VCSM_SQUARE_H +#define VCSM_SQUARE_H + +#include "RaspiTex.h" + +int vcsm_square_open(RASPITEX_STATE *state); + +#endif /* VCSM_SQUARE_H */ diff --git a/host_applications/linux/apps/raspicam/gl_scenes/yuv.c b/host_applications/linux/apps/raspicam/gl_scenes/yuv.c new file mode 100755 index 0000000..89fd6ab --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/yuv.c @@ -0,0 +1,145 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "yuv.h" +#include "RaspiTex.h" +#include "RaspiTexUtil.h" +#include +#include +#include + +/* Draw a scaled quad showing the the entire texture with the + * origin defined as an attribute */ +static RASPITEXUTIL_SHADER_PROGRAM_T yuv_shader = +{ + .vertex_source = + "attribute vec2 vertex;\n" + "attribute vec2 top_left;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " texcoord = vertex + vec2(0.0, 1.0);\n" + " gl_Position = vec4(top_left + vertex, 0.0, 1.0);\n" + "}\n", + + .fragment_source = + "#extension GL_OES_EGL_image_external : require\n" + "uniform samplerExternalOES tex;\n" + "varying vec2 texcoord;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(tex, texcoord);\n" + "}\n", + .uniform_names = {"tex"}, + .attribute_names = {"vertex", "top_left"}, +}; + +static GLfloat varray[] = +{ + 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, -1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, +}; + +static const EGLint yuv_egl_config_attribs[] = +{ + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE +}; + +/** + * Creates the OpenGL ES 2.X context and builds the shaders. + * @param raspitex_state A pointer to the GL preview state. + * @return Zero if successful. + */ +static int yuv_init(RASPITEX_STATE *state) +{ + int rc; + state->egl_config_attribs = yuv_egl_config_attribs; + rc = raspitexutil_gl_init_2_0(state); + if (rc != 0) + goto end; + + rc = raspitexutil_build_shader_program(&yuv_shader); + GLCHK(glUseProgram(yuv_shader.program)); + GLCHK(glUniform1i(yuv_shader.uniform_locations[0], 0)); // tex unit +end: + return rc; +} + +/** + * Draws a 2x2 grid with each shell showing the entire MMAL buffer from a + * different EGL image target. + */ +static int yuv_redraw(RASPITEX_STATE *raspitex_state) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + GLCHK(glUseProgram(yuv_shader.program)); + GLCHK(glActiveTexture(GL_TEXTURE0)); + GLCHK(glEnableVertexAttribArray(yuv_shader.attribute_locations[0])); + GLCHK(glVertexAttribPointer(yuv_shader.attribute_locations[0], + 2, GL_FLOAT, GL_FALSE, 0, varray)); + + // Y plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->y_texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], -1.0f, 1.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + // U plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->u_texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], 0.0f, 1.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + // V plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->v_texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], 0.0f, 0.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + // RGB plane + GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->texture)); + GLCHK(glVertexAttrib2f(yuv_shader.attribute_locations[1], -1.0f, 0.0f)); + GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); + + GLCHK(glDisableVertexAttribArray(yuv_shader.attribute_locations[0])); + GLCHK(glUseProgram(0)); + return 0; +} + +int yuv_open(RASPITEX_STATE *state) +{ + state->ops.gl_init = yuv_init; + state->ops.redraw = yuv_redraw; + state->ops.update_texture = raspitexutil_update_texture; + state->ops.update_y_texture = raspitexutil_update_y_texture; + state->ops.update_u_texture = raspitexutil_update_u_texture; + state->ops.update_v_texture = raspitexutil_update_v_texture; + return 0; +} diff --git a/host_applications/linux/apps/raspicam/gl_scenes/yuv.h b/host_applications/linux/apps/raspicam/gl_scenes/yuv.h new file mode 100755 index 0000000..b0e3a91 --- /dev/null +++ b/host_applications/linux/apps/raspicam/gl_scenes/yuv.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2013, Broadcom Europe Ltd +Copyright (c) 2013, Tim Gover +All rights reserved. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef YUV_H +#define YUV_H + +#include "RaspiTex.h" + +int yuv_open(RASPITEX_STATE *state); + +#endif /* YUV_H */ diff --git a/host_applications/linux/apps/raspicam/imv_examples/README.md b/host_applications/linux/apps/raspicam/imv_examples/README.md new file mode 100755 index 0000000..d85f343 --- /dev/null +++ b/host_applications/linux/apps/raspicam/imv_examples/README.md @@ -0,0 +1,39 @@ +Post-processing example programs to play with the imv buffer: +============================================================= + +Compile: +-------- +gcc imv2pgm.c -o imv2pgm -lm + +gcc imv2txt.c -o imv2txt + +Record and split buffer: +------------------------ +raspivid -x test.imv -o test.h264 + +We need to split the buffer first using split: + +split -a 4 -d -b $(((120+1)\*68\*4)) test.imv frame- + +Play: +----- +Now we can transform the velocity magnitues to a pgm image + +./imv2pgm frame-0001 120 68 frame-0001.pgm + +Or loop over all frames and create a movie + +for i in frame-????; do ./imv2pgm $i 120 68 $i.pgm;convert $i.pgm $i.png; rm $i.pgm; done + +avconv -i frame-%04d.png motion.avi + +And we can create a text file for easy plotting + +./imv2txt frame-0001 120 68 frame-0001.dat + +This textfile has in each line the center position x y of the macro block and +the velocities u and v and the sum of differences sad. + +These can be plot with xmgrace + +xmgrace -autoscale none -settype xyvmap frame-0001.dat -param plot.par diff --git a/host_applications/linux/apps/raspicam/imv_examples/imv2pgm.c b/host_applications/linux/apps/raspicam/imv_examples/imv2pgm.c new file mode 100755 index 0000000..fd32227 --- /dev/null +++ b/host_applications/linux/apps/raspicam/imv_examples/imv2pgm.c @@ -0,0 +1,88 @@ +/* +Copyright (c) 2014, Christian Kroener +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include + +typedef struct +{ + signed char x_vector; + signed char y_vector; + short sad; +} INLINE_MOTION_VECTOR; + +int main(int argc, const char **argv) +{ + if(argc!=5) + { + printf("usage: %s data.imv mbx mby out.pgm\n",argv[0]); + return 0; + } + int mbx=atoi(argv[2]); + int mby=atoi(argv[3]); + + /////////////////////////////// + // Read raw file to buffer // + /////////////////////////////// + FILE *f = fopen(argv[1], "rb"); + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + char *buffer = malloc(fsize + 1); + fread(buffer, fsize, 1, f); + fclose(f); + buffer[fsize] = 0; + + /////////////////// + // Fill struct // + /////////////////// + if(fsize<(mbx+1)*mby*4) + { + printf("File to short!\n"); + return 0; + } + INLINE_MOTION_VECTOR *imv; + imv = malloc((mbx+1)*mby*sizeof(INLINE_MOTION_VECTOR)); + memcpy ( &imv[0], &buffer[0], (mbx+1)*mby*sizeof(INLINE_MOTION_VECTOR) ); + + ///////////////////// + // Export to PGM // + ///////////////////// + FILE *out = fopen(argv[4], "w"); + fprintf(out,"P5\n%d %d\n255\n",mbx,mby); + int i,j; + for(j=0;j +#include +#include + +typedef struct +{ + signed char x_vector; + signed char y_vector; + short sad; +} INLINE_MOTION_VECTOR; + +int main(int argc, const char **argv) +{ + if(argc!=5) + { + printf("usage: %s data.imv mbx mby out.dat\n",argv[0]); + return 0; + } + int mbx=atoi(argv[2]); + int mby=atoi(argv[3]); + + /////////////////////////////// + // Read raw file to buffer // + /////////////////////////////// + FILE *f = fopen(argv[1], "rb"); + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + char *buffer = malloc(fsize + 1); + fread(buffer, fsize, 1, f); + fclose(f); + buffer[fsize] = 0; + + /////////////////// + // Fill struct // + /////////////////// + if(fsize<(mbx+1)*mby*4) + { + printf("File to short!\n"); + return 0; + } + INLINE_MOTION_VECTOR *imv; + imv = malloc((mbx+1)*mby*sizeof(INLINE_MOTION_VECTOR)); + memcpy ( &imv[0], &buffer[0], (mbx+1)*mby*sizeof(INLINE_MOTION_VECTOR) ); + + ////////////////////////// + // Export to txt data // + ////////////////////////// + FILE *out = fopen(argv[4], "w"); + fprintf(out,"#x y u v sad\n"); + int i,j; + for(j=0;j