Merge pull request #8132 from alphanu1/master

CRTSwitchRes Updates: Including Raspberry PI
This commit is contained in:
Twinaphex 2019-01-31 15:04:16 +01:00 committed by GitHub
commit 3d092c8394
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
823 changed files with 264172 additions and 5590 deletions

View File

@ -116,7 +116,7 @@ static void win32_display_server_destroy(void *data)
if (win32_orig_width > 0 && win32_orig_height > 0)
video_display_server_set_resolution(win32_orig_width, win32_orig_height,
win32_orig_refresh, (float)win32_orig_refresh, crt_center );
win32_orig_refresh, (float)win32_orig_refresh, crt_center, 0);
#ifdef HAS_TASKBAR_EXT
if (g_taskbarList)
@ -210,7 +210,7 @@ static bool win32_display_server_set_window_decorations(void *data, bool on)
}
static bool win32_display_server_set_resolution(void *data,
unsigned width, unsigned height, int int_hz, float hz, int center)
unsigned width, unsigned height, int int_hz, float hz, int center, int monitor_index)
{
DEVMODE curDevmode;
int iModeNum;

View File

@ -19,6 +19,10 @@
#include <sys/types.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h> // run pkg-config --static --libs xrandr
#include <X11/extensions/randr.h>
#include <X11/extensions/Xrender.h>
#include "../../config.h"
@ -35,11 +39,15 @@
static unsigned orig_width = 0;
static unsigned orig_height = 0;
static char old_mode[250] = {0};
static char orig_output[250] = {0};
static char new_mode[250] = {0};
static char xrandr[250] = {0};
static char fbset[150] = {0};
static char output[500] = {0};
static char output4[500] = {0};
static bool crt_en = false;
static unsigned crtid = 20;
static XRRModeInfo crt_rrmode;
typedef struct
{
@ -64,26 +72,23 @@ static void x11_display_server_destroy(void *data)
if (crt_en)
{
snprintf(output, sizeof(output),
"xrandr -s %dx%d", orig_width, orig_height);
"xrandr --newmode \"700x480_59.94\" 13.849698 700 742 801 867 480 490 496 533 interlace -hsync -vsync");
system(output);
snprintf(output, sizeof(output),
"xrandr --addmode %s 700x480_59.94", orig_output);
system(output);
snprintf(output, sizeof(output),
"xrandr --output %s --mode 700x480_59.94", orig_output);
system(output);
for (i = 0; i < 3; i++)
{
snprintf(output, sizeof(output),
"xrandr --delmode %s%d %s", "VGA", i, old_mode);
system(output);
snprintf(output, sizeof(output),
"xrandr --delmode %s-%d %s", "VGA", i, old_mode);
system(output);
snprintf(output, sizeof(output),
"xrandr --delmode %s %s",orig_output, old_mode);
system(output);
snprintf(output, sizeof(output),
"xrandr --delmode %s%d %s", "DVI", i, old_mode);
system(output);
snprintf(output, sizeof(output),
"xrandr --delmode %s-%d %s", "DVI", i, old_mode);
system(output);
}
snprintf(output, sizeof(output), "xrandr --rmmode %s", old_mode);
system(output);
@ -126,7 +131,7 @@ static bool x11_display_server_set_window_decorations(void *data, bool on)
}
static bool x11_display_server_set_resolution(void *data,
unsigned width, unsigned height, int int_hz, float hz, int center)
unsigned width, unsigned height, int int_hz, float hz, int center, int monitor_index)
{
int i = 0;
int hfp = 0;
@ -144,6 +149,13 @@ static bool x11_display_server_set_resolution(void *data,
float pixel_clock = 0;
crt_en = true;
sprintf(old_mode,"%s", new_mode);
Display* dsp = XOpenDisplay(NULL);
Screen* scrn = DefaultScreenOfDisplay(dsp);
XRRScreenResources *res;
int screen = DefaultScreen ( dsp );
Window window = RootWindow ( dsp, screen );
/* set core refresh from hz */
video_monitor_set_refresh_rate(hz);
@ -235,59 +247,67 @@ static bool x11_display_server_set_resolution(void *data,
/* need to run loops for DVI0 - DVI-2 and VGA0 - VGA-2 outputs to
* add and delete modes */
for (i = 0; i < 3; i++)
crt_rrmode.id = crtid;
crt_rrmode.width = width;
crt_rrmode.height = height;
crt_rrmode.dotClock = pixel_clock;
crt_rrmode.hSyncStart = hfp;
crt_rrmode.hSyncEnd = hsp;
crt_rrmode.hTotal = hmax;
crt_rrmode.hSkew = 0;
crt_rrmode.vSyncStart = vfp;
crt_rrmode.vSyncEnd = vsp;
crt_rrmode.vTotal = vmax;
crt_rrmode.name = new_mode;
crt_rrmode.nameLength = sizeof(new_mode);
crt_rrmode.modeFlags = 0;
res = XRRGetScreenResources (dsp, window);
if (monitor_index == 0)
{
snprintf(output, sizeof(output), "xrandr --addmode %s%d %s", "DVI", i,
new_mode);
system(output);
snprintf(output, sizeof(output), "xrandr --delmode %s%d %s", "DVI", i,
old_mode);
system(output);
}
for (i = 0; i < 3; i++)
{
snprintf(output, sizeof(output), "xrandr --addmode %s-%d %s", "DVI", i,
new_mode);
system(output);
snprintf(output, sizeof(output), "xrandr --delmode %s-%d %s", "DVI", i,
old_mode);
system(output);
}
for (i = 0; i < 3; i++)
{
snprintf(output, sizeof(output), "xrandr --addmode %s%d %s", "VGA", i, new_mode);
system(output);
snprintf(output, sizeof(output), "xrandr --delmode %s%d %s", "VGA", i, old_mode);
system(output);
}
for (i = 0; i < 3; i++)
{
snprintf(output, sizeof(output), "xrandr --addmode %s-%d %s", "VGA", i, new_mode);
system(output);
snprintf(output, sizeof(output), "xrandr --delmode %s-%d %s", "VGA", i, old_mode);
system(output);
}
snprintf(output, sizeof(output), "xrandr -s %s", new_mode);
system(output);
/* remove old mode */
snprintf(output, sizeof(output), "xrandr --rmmode %s", old_mode);
system(output);
/* needs xdotool installed. needed to recapture window. */
system("xdotool windowactivate $(xdotool search --class RetroArch)");
/* variable for old mode */
snprintf(old_mode, sizeof(old_mode), "%s", new_mode);
/* needs xdotool installed. needed to recapture window. */
system("xdotool windowactivate $(xdotool search --class RetroArch)");
/* Second run needed as some times it runs to fast to capture first time */
for (int i = 0; i < res->noutput; i++)
{
XRROutputInfo *outputs = XRRGetOutputInfo (dsp, res, res->outputs[i]);
if (outputs->connection == RR_Connected)
{
snprintf(output4, sizeof(output4),"xrandr --addmode %s %s",outputs->name ,new_mode);
system(output4);
snprintf(output4, sizeof(output4),"xrandr --output %s --mode %s", outputs->name, new_mode);
system(output4);
snprintf(output4, sizeof(output4),"xrandr --delmode %s %s", outputs->name,old_mode);
system(output4);
snprintf(output4, sizeof(output4),"xrandr --rmmode %s", old_mode);
system(output4);
}
}
}
if (monitor_index > 0)
{
XRROutputInfo *outputs = XRRGetOutputInfo (dsp, res, res->outputs[monitor_index]);
if (outputs->connection == RR_Connected)
{
snprintf(orig_output, sizeof(orig_output),"%s", outputs->name);
snprintf(output4, sizeof(output4),"xrandr --addmode %s %s",outputs->name ,new_mode);
system(output4);
snprintf(output4, sizeof(output4),"xrandr --output %s --mode %s", outputs->name, new_mode);
system(output4);
snprintf(output4, sizeof(output4),"xrandr --delmode %s %s", outputs->name, old_mode);
system(output4);
snprintf(output4, sizeof(output4),"xrandr --rmmode %s", old_mode);
system(output4);
}
}
return true;
}

32
gfx/include/userland/.gitignore vendored Normal file
View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -0,0 +1,8 @@
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
Note that this repository does not contain the source for the edidparser and vcdbg binaries due to licensing restrictions.

View File

@ -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

View File

@ -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)

View File

@ -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})

File diff suppressed because it is too large Load Diff

View File

@ -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 <stdlib.h>
#include <string.h>
//#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

View File

@ -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})

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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})

View File

@ -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 <stdlib.h>
#include <string.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"
/******************************************************************************
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

View File

@ -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 <stdlib.h>
#include <string.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"
/******************************************************************************
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

View File

@ -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 */

View File

@ -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 */

View File

@ -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 <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stddef.h>
#if defined( __STDC__ ) && defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#include <stdint.h>
#include <inttypes.h>
#elif defined( __GNUC__ )
#include <stdint.h>
#include <inttypes.h>
#elif defined(_MSC_VER)
#include <stdint.h>
#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 */

View File

@ -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 <stdlib.h>
#include <string.h>
#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;
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 <stdlib.h>
#include <string.h>
#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;
}

View File

@ -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 */

View File

@ -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 <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#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
}

View File

@ -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 <stdarg.h>
#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 */

View File

@ -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 <stdlib.h>
#include <string.h>
#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));
if(id == NULL) { goto error; }
memset(id, 0, sizeof(VC_CONTAINER_INDEX_T));
id->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<<index->max_count))
{
int entry;
if(index->next == (1<<index->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;
}

View File

@ -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 */

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#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;
}

View File

@ -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 */
/*-----------------------------------------------------------------------------*/

View File

@ -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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#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;
}
}

View File

@ -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_ */

View File

@ -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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#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;
}

View File

@ -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 */

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#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 <cutils/log.h>
#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
}

View File

@ -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 */

View File

@ -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 <stdarg.h>
#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 */

View File

@ -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 */

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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 <stdlib.h>
#include <string.h>
#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;
}
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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 <stdlib.h>
#include <string.h>
#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 <stdio.h>
/*****************************************************************************/
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;
}

View File

@ -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 */

View File

@ -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 <stdlib.h>
#include <string.h>
#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;
}

View File

@ -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 <stdarg.h>
#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(&registry_entry); }
/* @} */
#endif /* VC_PACKETIZERS_PRIVATE_H */

View File

@ -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})

View File

@ -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 <stdlib.h>
#include <string.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"
/******************************************************************************
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

View File

@ -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})

File diff suppressed because it is too large Load Diff

View File

@ -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 <stdlib.h>
#include <string.h>
#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");

View File

@ -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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#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;
}

View File

@ -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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#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;
}

View File

@ -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 <stdlib.h>
#include <string.h>
#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 <stdio.h>
#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;
}

View File

@ -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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#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;
}

View File

@ -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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#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;
}

View File

@ -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})

View File

@ -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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#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

View File

@ -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"
};

View File

@ -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})

File diff suppressed because it is too large Load Diff

View File

@ -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})

View File

@ -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 */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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})

File diff suppressed because it is too large Load Diff

View File

@ -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})

View File

@ -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;
}

View File

@ -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 <stdlib.h>
#include <string.h>
#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");

View File

@ -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 <stdlib.h>
#include <string.h>
#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

View File

@ -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 <stdlib.h>
#include <string.h>
#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");

View File

@ -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 */

View File

@ -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 <errno.h>
#include <unistd.h>
#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;
}

View File

@ -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 <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <netdb.h>
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_ */

View File

@ -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 <stdio.h>
#include <stdlib.h>
#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);
}

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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 <winsock2.h>
#include <ws2tcpip.h>
typedef SOCKET SOCKET_T;
typedef int SOCKADDR_LEN_T;
typedef char *SOCKOPT_CAST_T;
#endif /* _NET_SOCKETS_WIN32_H_ */

View File

@ -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 */

View File

@ -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 <stdlib.h>
#include <string.h>
#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");

View File

@ -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})

View File

@ -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 <stdlib.h>
#include <string.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"
/******************************************************************************
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<tracks; i++)
{
uint32_t len, ticks, tempo_ticks;
int64_t time;
uint8_t dummy[8];
if(READ_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy) ||
dummy[0] != 'M' || dummy[1] != 'T' || dummy[2] != 'r' || dummy[3] != 'k')
return VC_CONTAINER_ERROR_FORMAT_INVALID;
len = BI32(dummy+4);
// impose a 1mb limit on track size
if(len > (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

View File

@ -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})

View File

@ -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 */

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.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"
#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

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.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"
#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

View File

@ -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})

View File

@ -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 <stdlib.h>
#include <string.h>
#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, &timestamp, &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

View File

@ -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})

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#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, &param) || !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;
}

View File

@ -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_ */

Some files were not shown because too many files have changed in this diff Show More